原理:
用數組存儲數字,按照計算法則進行運算。
代碼:
package com.hdwang; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 大數四則運算(超出long型的大數(64位:18446744073709551615)) * Created by hdwang on 2017/10/9. */ public class Calculator { /** * 兩數相加 * @param numStr1 數1 * @param numStr2 數2 * @return 結果 */ public static String add(String numStr1, String numStr2){ int numLen1 = numStr1.length(); int numLen2 = numStr2.length(); int[] numArray1 = new int[numLen1]; //數字數組 int[] numArray2 = new int[numLen2]; // "12345"-> [5,4,3,2,1] for(int i=0;i<numLen1;i++){ String c = numStr1.substring(i,i+1); numArray1[numLen1-i-1] = Integer.parseInt(c); //低位存字符串尾部數字 } for(int i=0;i<numLen2;i++){ String c = numStr2.substring(i,i+1); numArray2[numLen2-i-1] = Integer.parseInt(c); //低位存字符串尾部數字 } int minLen = 0; //取長度小的數位數 int maxLen = 0; //取長度大的數位數 int[] maxArray = null; //長度大的數 if(numLen1<numLen2){ minLen = numLen1; maxLen = numLen2; maxArray = numArray2; }else{ minLen = numLen2; maxLen = numLen1; maxArray = numArray1; } int[] resultArray = new int[maxLen+1]; //考慮到可能會進位,多給一個元素空間 //兩數長度相同的部分,同位相加,超出9進1 int added = 0; int i=0; for(;i<minLen;i++){ int t = numArray1[i]+numArray2[i]+added; //兩數相加,再加進位 if(t>9){ added = 1; //進1 resultArray[i] = t-10; //當前位計算結果 }else{ added = 0; //不進位 resultArray[i] = t; //當前位計算結果 } } //長度超出部分累加 for(;i<maxLen;i++){ int t = maxArray[i]+added; //多余位數加上進位 if(t>9){ added = 1; //進1 resultArray[i] = t-10; //當前位計算結果 }else{ added = 0; //不進位 resultArray[i] = t; //當前位計算結果 } } resultArray[i] = added; //最高位 //拼接結果 [1,4,8,2,0] -> 2841 StringBuilder builder = new StringBuilder(); for(int n=resultArray.length-1;n>=0;n--){ //如果最高位為0,移除 if(n==resultArray.length-1 && resultArray[resultArray.length-1]==0){ continue; //跳過 }else{ builder.append(resultArray[n]); } } return builder.toString(); } /** * 兩數相減 * @param numStr1 數1 * @param numStr2 數2 * @return 結果 */ public static String subtract(String numStr1,String numStr2){ int numLen1 = numStr1.length(); int numLen2 = numStr2.length(); int[] numArray1 = new int[numLen1]; //數字數組 int[] numArray2 = new int[numLen2]; // "12345"-> [5,4,3,2,1] for(int i=0;i<numLen1;i++){ String c = numStr1.substring(i,i+1); numArray1[numLen1-i-1] = Integer.parseInt(c); //低位存字符串尾部數字 } for(int i=0;i<numLen2;i++){ String c = numStr2.substring(i,i+1); numArray2[numLen2-i-1] = Integer.parseInt(c); //低位存字符串尾部數字 } int minLen = 0; //取長度小的數位數 int maxLen = 0; //取長度大的數位數 int[] maxArray = null; //數值大的數 if(numLen1<numLen2){ minLen = numLen1; maxLen = numLen2; maxArray = numArray2; }else{ minLen = numLen2; maxLen = numLen1; maxArray = numArray1; if(numLen1 == numLen2){ //等於 maxArray = getMaxNumber(numArray1,numArray2); } } int[] minArray = maxArray==numArray1?numArray2:numArray1; //數值小的數 int[] resultArray = new int[maxLen]; //大數-小數,同位相減,小於0借位 int subtracted = 0; int i=0; for(;i<minLen;i++){ int t = maxArray[i] - minArray[i] - subtracted; //兩數相減,再減借位 if(t<0){ subtracted = 1; //向高位借1,暫存起來 resultArray[i] = t+10; //當前位計算結果(借1相當於借了10) }else{ subtracted = 0; //不借位 resultArray[i] = t; //當前位計算結果 } } //大數超出部分減掉借位 for(;i<maxLen;i++){ int t = maxArray[i]-subtracted; //多余位數減掉借位 if(t<0){ subtracted = 1; //進1 resultArray[i] = t+10; //當前位計算結果 }else{ subtracted = 0; //不借位 resultArray[i] = t; //當前位計算結果 } } //拼接結果 [1,4,8,2,0] -> 2841 StringBuilder builder = new StringBuilder(); boolean highBitNotEqualZero = false; //存在高位不為0的情況,低位0保留 for(int n=resultArray.length-1;n>=0;n--){ //如果高位為0,移除 if(resultArray[n]==0 && !highBitNotEqualZero && n!=0){ //高位無用的0去除 continue; //跳過 }else{ highBitNotEqualZero = true; //找到不為0的位 builder.append(resultArray[n]); } } if(maxArray == numArray1){ //第一個數大或相等 }else{ //第一個數小於第二個數,相減為負數 builder.insert(0,"-"); } return builder.toString(); } /** * 兩數相乘 * @param numStr1 數1 * @param numStr2 數2 * @return 結果 */ public static String multiply(String numStr1,String numStr2){ int numLen1 = numStr1.length(); int numLen2 = numStr2.length(); int[] numArray1 = new int[numLen1]; //數字數組 int[] numArray2 = new int[numLen2]; // "12345"-> [5,4,3,2,1] for(int i=0;i<numLen1;i++){ String c = numStr1.substring(i,i+1); numArray1[numLen1-i-1] = Integer.parseInt(c); //低位存字符串尾部數字 } for(int i=0;i<numLen2;i++){ String c = numStr2.substring(i,i+1); numArray2[numLen2-i-1] = Integer.parseInt(c); //低位存字符串尾部數字 } int minLen = 0; //取長度小的數位數 int maxLen = 0; //取長度大的數位數 int[] maxArray = null; //長度大的數 int[] minArray = null; //長度小的數 if(numLen1<numLen2){ minLen = numLen1; maxLen = numLen2; minArray = numArray1; maxArray = numArray2; }else{ minLen = numLen2; maxLen = numLen1; minArray = numArray2; maxArray = numArray1; } //二維數組存儲結果,例如:23*23 ->[[6,9],[4,6]] ,內部括號(低維)存某位的相乘結果,高維低位存個位,十位... int[][] resultArray = new int[minLen][maxLen+1]; //長度大的數*長度小的數的每一位,分別存到相應數組中,然后累加 for(int h=0;h<minLen;h++){ //高維 int l=0; int added = 0; for(;l<maxLen;l++){ //低維 int t = maxArray[l]*minArray[h]+added; //長度大的數的每一位*長度小的數的個位、十位... if(t>9){ added = t/10; //進位 resultArray[h][l] = t%10; //當前位計算結果 }else{ added = 0; //不進位 resultArray[h][l] = t; //當前位計算結果 } } resultArray[h][l] = added; //個位、十位...的計算結果的最高位 } //對結果補位(左移),個位不動,十位補0,百位補00...,然后累加 int[] sum = null; //最終累加結果 int[] lowBitResult = null; //低位補0結果(前一位) for(int h=0;h<minLen;h++){ int[] bitResult = resultArray[h]; int[] r; //個位、十位...的補0結果 if(h==0){ //個位 r = bitResult; sum = r; lowBitResult = r; //記錄下來,待下次循環累加 }else{ //十位...的計算結果 r = new int[resultArray[h].length+h]; //初始化默認就是0的 int rLen = r.length-1; for(int i=bitResult.length-1;i>=0;i--){ //從高位開始復制到新數組 r[rLen--] = bitResult[i]; } //累加之前的數 sum = new int[r.length+1]; //取高位長度+1,可能進位 //================加法核心算法==================== //兩數長度相同的部分,同位相加,超出9進1 int added = 0; int i=0; for(;i<lowBitResult.length;i++){ int t = lowBitResult[i]+r[i]+added; //兩數相加,再加進位 if(t>9){ added = 1; //進1 sum[i] = t-10; //當前位計算結果 }else{ added = 0; //不進位 sum[i] = t; //當前位計算結果 } } //長度超出部分累加 for(;i<r.length;i++){ int t = r[i]+added; //多余位數加上進位 if(t>9){ added = 1; //進1 sum[i] = t-10; //當前位計算結果 }else{ added = 0; //不進位 sum[i] = t; //當前位計算結果 } } sum[i] = added; //最高位 //=============================================== lowBitResult = sum; //記錄下來,待下次循環累加 } } //拼接結果 [1,4,8,2,0] -> 2841 StringBuilder builder = new StringBuilder(); boolean existHighNotZero = false; //高位存在不為0的,這個0就不能移除 for(int n=sum.length-1;n>=0;n--){ //移除高位無效的0,保留最后一個0 if(sum[n]==0 && !existHighNotZero && n!=0){ continue; //跳過 }else{ existHighNotZero = true; builder.append(sum[n]); } } return builder.toString(); } /** * 兩數相除 * @param numStr1 數1(被除數) * @param numStr2 數2(除數,不能超過long型) * @return 結果 */ public static String divide(String numStr1,String numStr2){ int numLen1 = numStr1.length(); int numLen2 = numStr2.length(); int[] numArray1 = new int[numLen1]; //數字數組 int[] numArray2 = new int[numLen2]; // "12345"-> [5,4,3,2,1] for(int i=0;i<numLen1;i++){ String c = numStr1.substring(i,i+1); numArray1[numLen1-i-1] = Integer.parseInt(c); //低位存字符串尾部數字 } for(int i=0;i<numLen2;i++){ String c = numStr2.substring(i,i+1); numArray2[numLen2-i-1] = Integer.parseInt(c); //低位存字符串尾部數字 } int effectiveNum = (numLen1 >= numLen2 ? numLen1:numLen2)+16; //有效位數: 默認大數長度+16 int[] resultArray = new int[effectiveNum]; //高位存高位 //將被除數的每一位除以除數,取整為該位結果,取余暫存借給低位(除數不能大過long型,除非除法轉換為減法) long yu = 0; int resultIndex = effectiveNum-1; for(int i=numArray1.length-1;i>=0;i--){ long num = yu * 10 + numArray1[i]; //被除數該位為:余數*10+自己 int r= (int)(num / Long.parseLong(numStr2)); //取整 yu = num % Long.parseLong(numStr2); //取余 resultArray[resultIndex--] = r; } int decimalPoint = effectiveNum-numArray1.length-1; //小數點位置 if(yu!=0){ int decimal = decimalPoint; //小數 for(int i=0;i<effectiveNum-numArray1.length;i++){ long num = yu * 10 + 0; //小數部分被除數補0 int r= (int)(num / Long.parseLong(numStr2)); //取整 yu = num % Long.parseLong(numStr2); //取余 resultArray[decimal--] = r; if(yu==0){ break; //余數為0,提前退出 } } } //拼接結果 StringBuilder builder = new StringBuilder(); boolean existHighNotZero = false; for(int i=effectiveNum-1;i>=0;i--){ if(i==decimalPoint){ builder.append("."); } if(resultArray[i]==0){ if(!existHighNotZero && i>decimalPoint+1){ //跳過高位無用的0 continue; } }else{ existHighNotZero = true; } builder.append(resultArray[i]); } String result = builder.toString(); //去除尾部無用的0 int endIndex = result.length(); for(int i=result.length()-1;i>=0;i--){ char c = result.charAt(i); if(c!='0'){ endIndex = i+1; break; } } //去除多余的小數點 if(result.charAt(endIndex-1)=='.'){ endIndex = endIndex-1; } result = result.substring(0,endIndex); return result; } /** * 兩數相除(增強版) * @param numStr1 數1(被除數) * @param numStr2 數2(除數) * @return 結果 */ public static String divideEnhanced(String numStr1,String numStr2){ int numLen1 = numStr1.length(); int numLen2 = numStr2.length(); int[] numArray1 = new int[numLen1]; //數字數組 int[] numArray2 = new int[numLen2]; // "12345"-> [5,4,3,2,1] for(int i=0;i<numLen1;i++){ String c = numStr1.substring(i,i+1); numArray1[numLen1-i-1] = Integer.parseInt(c); //低位存字符串尾部數字 } for(int i=0;i<numLen2;i++){ String c = numStr2.substring(i,i+1); numArray2[numLen2-i-1] = Integer.parseInt(c); //低位存字符串尾部數字 } int effectiveNum = (numLen1 >= numLen2 ? numLen1:numLen2)+16; //有效位數: 默認大數長度+16 int[] resultArray = new int[effectiveNum]; //高位存高位 //將被除數的每一位除以除數,取整為該位結果,取余暫存借給低位(除數不能大過long型,除非除法轉換為減法) String yu = "0"; int resultIndex = effectiveNum-1; for(int i=numArray1.length-1;i>=0;i--){ String num = "0".equals(yu)?numArray1[i]+"":add(yu+"0",numArray1[i]+""); //被除數該位為:余數*10+自己 DivideResult result = getDivideResult(num,numStr2); String r= result.getR() ; //取整 yu = result.getYu(); //取余 resultArray[resultIndex--] = Integer.parseInt(r); //某位上的結果肯定小於10 } int decimalPoint = effectiveNum-numArray1.length-1; //小數點位置 if(!"0".equals(yu)){ int decimal = decimalPoint; //小數 for(int i=0;i<effectiveNum-numArray1.length;i++){ String num = yu+"0"; //小數部分被除數補0 DivideResult result = getDivideResult(num,numStr2); String r= result.getR() ; //取整 yu = result.getYu(); //取余 resultArray[decimal--] = Integer.parseInt(r); if("0".equals(yu)){ break; //余數為0,提前退出 } } } //拼接結果 StringBuilder builder = new StringBuilder(); boolean existHighNotZero = false; for(int i=effectiveNum-1;i>=0;i--){ if(i==decimalPoint){ builder.append("."); } if(resultArray[i]==0){ if(!existHighNotZero && i>decimalPoint+1){ //跳過高位無用的0 continue; } }else{ existHighNotZero = true; } builder.append(resultArray[i]); } String result = builder.toString(); //去除尾部無用的0 int endIndex = result.length(); for(int i=result.length()-1;i>=0;i--){ char c = result.charAt(i); if(c!='0'){ endIndex = i+1; break; } } //去除多余的小數點 if(result.charAt(endIndex-1)=='.'){ endIndex = endIndex-1; } result = result.substring(0,endIndex); return result; } /** * 校驗數字是否合法 * @param numStr 數字字符串 * @return 是否合法 */ public static boolean numberValid(String numStr){ Pattern pattern = Pattern.compile("^[1-9]\\d*$|0"); Matcher matcher = pattern.matcher(numStr); return matcher.matches(); } /** * 計算大數 * @param numArray1 數1 * @param numArray2 數2 * @return 大數 */ public static int[] getMaxNumber(int[] numArray1, int[] numArray2) { for(int i=numArray1.length-1;i>=0;i--){ if(numArray1[i]>numArray2[i]){ return numArray1; }else{ if(numArray1[i]==numArray2[i]){ continue; //待繼續比較 }else{ return numArray2; } } } return numArray1; //全部相等,返回第一個 } /** * 除法轉換為減法 * @param numStr1 數1(被除數) * @param numStr2 數2(除數) * @return 除的結果 */ public static DivideResult getDivideResult(String numStr1,String numStr2){ DivideResult result = new DivideResult(); String r = ""; // String times = "0"; int times = 0; //取整不會大於9的(被除數(余數+某位)/除數(肯定大於余數)這個過程是,被除數逐漸增大到可以除以除數為止,此時被除數>=除數,剛剛好,所以被除數最多比除數多1位,兩數相差肯定小於10倍) while (true){ r=subtract(numStr1,numStr2); // times = add(times,"1"); //次數遞增 times++; if("0".equals(r)){ //除盡了 result.setYu("0"); result.setR(times+""); break; }else if(r.startsWith("-")){ //負數,多減了一次 result.setYu(numStr1); //上次減下來多余的數值,就是余數 // result.setR(subtract(times,"1")); result.setR((times-1)+""); break; } numStr1 = r; //被減數重置為剩余的數值 } return result; } }
package com.hdwang; /** * Created by hdwang on 2017/10/10. * 相除結果 */ public class DivideResult { /** * 取整結果 */ private String r; /** * 取余結果 */ private String yu; public String getR() { return r; } public void setR(String r) { this.r = r; } public String getYu() { return yu; } public void setYu(String yu) { this.yu = yu; } }
package com.hdwang; import java.util.Scanner; import static com.hdwang.Calculator.*; public class Main { public static void main(String[] args) { // write your code here Scanner scanner = new Scanner(System.in); boolean loop = true; while (loop) { System.out.println("請輸入第一個非負整數:"); String numStr1 = scanner.nextLine(); if (!numberValid(numStr1)) { System.out.println(String.format("%s不合法", numStr1)); continue; } System.out.println("請輸入第二個非負整數:"); String numStr2 = scanner.nextLine(); if (!numberValid(numStr2)) { System.out.println(String.format("%s不合法", numStr2)); continue; } String r1 = add(numStr1, numStr2); System.out.println(String.format("大數加法計算:%s+%s%s=%s", numStr1, numStr2,r1.length()>50?"\n":"", r1)); try { System.out.println("加法直接計算:" + (Long.parseLong(numStr1) + Long.parseLong(numStr2))); }catch (Exception ex){ System.out.println("加法直接計算:"+ex.getClass().getName()); } String r2 = subtract(numStr1, numStr2); System.out.println(String.format("大數減法計算:%s-%s%s=%s", numStr1, numStr2,r2.length()>50?"\n":"", r2)); try { System.out.println("減法直接計算:" + (Long.parseLong(numStr1) - Long.parseLong(numStr2))); }catch (Exception ex){ System.out.println("減法直接計算:"+ex.getClass().getName()); } String r3 = multiply(numStr1, numStr2); System.out.println(String.format("大數乘法計算:%s*%s%s=%s", numStr1, numStr2,r3.length()>50?"\n":"", r3)); try { System.out.println("乘法直接計算:" + (Long.parseLong(numStr1) * Long.parseLong(numStr2))); }catch (Exception ex){ System.out.println("乘法直接計算:"+ex.getClass().getName()); } try { String r4 = divide(numStr1, numStr2); System.out.println(String.format("大數除法計算:%s/%s%s=%s", numStr1, numStr2, r4.length() > 50 ? "\n" : "", r4)); }catch (Exception ex){ System.out.println("大數除法計算:"+ex.getClass().getName()); } try { System.out.println("除法直接計算:" + ((double)Long.parseLong(numStr1) / (double) Long.parseLong(numStr2))); }catch (Exception ex){ System.out.println("除法直接計算:"+ex.getClass().getName()); } String r5 = divideEnhanced(numStr1, numStr2); System.out.println(String.format("增強版大數除法計算:%s/%s%s=%s", numStr1, numStr2,r5.length()>50?"\n":"", r5)); System.out.println("退出輸入q,否則繼續"); String line = scanner.nextLine(); if(line.equalsIgnoreCase("Q")){ loop = false; }else{ loop = true; } } } }
運行結果:
請輸入第一個非負整數: 1 請輸入第二個非負整數: 1 大數加法計算:1+1=2 加法直接計算:2 大數減法計算:1-1=0 減法直接計算:0 大數乘法計算:1*1=1 乘法直接計算:1 大數除法計算:1/1=1 除法直接計算:1.0 增強版大數除法計算:1/1=1 退出輸入q,否則繼續 請輸入第一個非負整數: 2 請輸入第二個非負整數: 3 大數加法計算:2+3=5 加法直接計算:5 大數減法計算:2-3=-1 減法直接計算:-1 大數乘法計算:2*3=6 乘法直接計算:6 大數除法計算:2/3=0.6666666666666666 除法直接計算:0.6666666666666666 增強版大數除法計算:2/3=0.6666666666666666 退出輸入q,否則繼續 請輸入第一個非負整數: 25 請輸入第二個非負整數: 25 大數加法計算:25+25=50 加法直接計算:50 大數減法計算:25-25=0 減法直接計算:0 大數乘法計算:25*25=625 乘法直接計算:625 大數除法計算:25/25=1 除法直接計算:1.0 增強版大數除法計算:25/25=1 退出輸入q,否則繼續 請輸入第一個非負整數: 100 請輸入第二個非負整數: 50 大數加法計算:100+50=150 加法直接計算:150 大數減法計算:100-50=50 減法直接計算:50 大數乘法計算:100*50=5000 乘法直接計算:5000 大數除法計算:100/50=2 除法直接計算:2.0 增強版大數除法計算:100/50=2 退出輸入q,否則繼續 請輸入第一個非負整數: 3 請輸入第二個非負整數: 4 大數加法計算:3+4=7 加法直接計算:7 大數減法計算:3-4=-1 減法直接計算:-1 大數乘法計算:3*4=12 乘法直接計算:12 大數除法計算:3/4=0.75 除法直接計算:0.75 增強版大數除法計算:3/4=0.75 退出輸入q,否則繼續 請輸入第一個非負整數: 4 請輸入第二個非負整數: 3 大數加法計算:4+3=7 加法直接計算:7 大數減法計算:4-3=1 減法直接計算:1 大數乘法計算:4*3=12 乘法直接計算:12 大數除法計算:4/3=1.3333333333333333 除法直接計算:1.3333333333333333 增強版大數除法計算:4/3=1.3333333333333333 退出輸入q,否則繼續 請輸入第一個非負整數: 1 請輸入第二個非負整數: 100 大數加法計算:1+100=101 加法直接計算:101 大數減法計算:1-100=-99 減法直接計算:-99 大數乘法計算:1*100=100 乘法直接計算:100 大數除法計算:1/100=0.01 除法直接計算:0.01 增強版大數除法計算:1/100=0.01 退出輸入q,否則繼續 請輸入第一個非負整數: 100 請輸入第二個非負整數: 1 大數加法計算:100+1=101 加法直接計算:101 大數減法計算:100-1=99 減法直接計算:99 大數乘法計算:100*1=100 乘法直接計算:100 大數除法計算:100/1=100 除法直接計算:100.0 增強版大數除法計算:100/1=100 退出輸入q,否則繼續 請輸入第一個非負整數: 1 請輸入第二個非負整數: 10000000000 大數加法計算:1+10000000000=10000000001 加法直接計算:10000000001 大數減法計算:1-10000000000=-9999999999 減法直接計算:-9999999999 大數乘法計算:1*10000000000=10000000000 乘法直接計算:10000000000 大數除法計算:1/10000000000=0.0000000001 除法直接計算:1.0E-10 增強版大數除法計算:1/10000000000=0.0000000001 退出輸入q,否則繼續 請輸入第一個非負整數: 1 請輸入第二個非負整數: 100000000000000000000000000000000000000000000000000 大數加法計算:1+100000000000000000000000000000000000000000000000000 =100000000000000000000000000000000000000000000000001 加法直接計算:java.lang.NumberFormatException 大數減法計算:1-100000000000000000000000000000000000000000000000000 =-99999999999999999999999999999999999999999999999999 減法直接計算:java.lang.NumberFormatException 大數乘法計算:1*100000000000000000000000000000000000000000000000000 =100000000000000000000000000000000000000000000000000 乘法直接計算:java.lang.NumberFormatException 大數除法計算:java.lang.NumberFormatException 除法直接計算:java.lang.NumberFormatException 增強版大數除法計算:1/100000000000000000000000000000000000000000000000000 =0.00000000000000000000000000000000000000000000000001 退出輸入q,否則繼續
說明:
當數字的大小超過long類型的數值范圍時,將無法對數值進行計算,所以必須實現一套算法。曾經上C語言程序設計時做的一套課程設計就是這個題目,當時苦於能力不足,寫不起來。如今,用java寫出來了加減運算,經過多次測試,准確無誤。耗時8.5小時完成,哈哈。至於小數計算,可以轉換成整數計算,然后移動小數點位置即可。至於符號帶符號的運算也簡單,乘除法負負得正,正負得負,正正得正,帶負號的加減法可以轉換為不待負號的運算。例如:-a-b=-(a+b) ; -a-(-b) = b-a; -a + -b = -(a+b) 等
