Java 高精度的大数字运算
为了解决Java基本数据类型在运算时会出现的溢出和计算不精确的问题。Java 提供了两个类BigInteger和BigDecimal,专门用于进行高精度运算。凡是能用int 或float 做的事情,用BigInteger和BigDecimal也可以做,只是必须换用方法调用,而不是使用运算符。
高精度整数BigInteger
BigInteger支持任意精度的整数,也就是说我们可精确表示任意大小的整数值;同时在运算过程中不会丢失任何信息;
高精度浮点数BigDecimal
它可以表示任意精度的小数,并对它们进行计算。由于 BigDecimal 对象是不可变的,这些方法中的每一个都会产生新的 BigDecimal 对象。因此,因为创建对象的开销,BigDecimal 不适合于大量的数学计算,但设计它的目的是用来精确地表示小数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
import java.math.BigDecimal; import java.math.BigInteger; public class BigNumber { //默认除法运算精度,即保留小数点多少位 private static final int DEFAULT_DIV_SCALE = 10 ; //这个类不能实例化 private BigNumber() { } /** * 提供精确的加法运算。 * @param v1 被加数 * @param v2 加数 * @return 两个参数的和 */ public static double add( double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return (b1.add(b2)).doubleValue(); } /** * 提供精确的减法运算。 * @param v1 被减数 * @param v2 减数 * @return 两个参数的差 */ public static double sub( double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return (b1.subtract(b2)).doubleValue(); } /** * 提供精确的乘法运算。 * @param v1 被乘数 * @param v2 乘数 * @return 两个参数的积 */ public static double mul( double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return (b1.multiply(b2)).doubleValue(); } /** * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 * 小数点以后多少位,以后的数字四舍五入。 * @param v1 被除数 * @param v2 除数 * @return 两个参数的商 */ public static double div( double v1, double v2) { return div(v1, v2, DEFAULT_DIV_SCALE); } /** * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 * 定精度,以后的数字四舍五入。 * @param v1 被除数 * @param v2 除数 * @param scale 表示需要精确到小数点以后几位。 * @return 两个参数的商 */ public static double div( double v1, double v2, int scale) { if (scale < 0 ) { System.err.println( "除法精度必须大于0!" ); return 0 ; } BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return (b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP)).doubleValue(); } /** * 计算Factorial阶乘! * @param n 任意大于等于0的int * @return n!的值 */ public static BigInteger getFactorial( int n) { if (n < 0 ) { System.err.println( "n必须大于等于0!" ); return new BigInteger( "-1" ); } else if (n == 0 ) { return new BigInteger( "0" ); } //将数组换成字符串后构造BigInteger BigInteger result = new BigInteger( "1" ); for (; n > 0 ; n--) { //将数字n转换成字符串后,再构造一个BigInteger对象,与现有结果做乘法 result = result.multiply( new BigInteger( new Integer(n).toString())); } return result; } public static void main(String[] args) { // 如果我们编译运行下面这个程序会看到什么? System.out.println( 0.05 + 0.01 ); System.out.println( 1.0 - 0.42 ); System.out.println( 4.015 * 100 ); System.out.println( 123.3 / 100 ); // 0.060000000000000005 // 0.5800000000000001 // 401.49999999999994 // 1.2329999999999999 //计算阶乘,可以将n设得更大 int n = 30 ; System.out.println( "计算n的阶乘" + n + "! = " + BigNumber.getFactorial(n)); //用double构造BigDecimal BigDecimal bd1 = new BigDecimal( 0.1 ); System.out.println( "(bd1 = new BigDecimal(0.1)) = " + bd1.toString()); //用String构造BigDecimal BigDecimal bd2 = new BigDecimal( "0.1" ); System.out.println( "(bd2 = new BigDecimal(\"0.1\")) = " + bd2.toString()); BigDecimal bd3 = new BigDecimal( "0.10" ); //equals方法比较两个BigDecimal对象是否相等,相等返回true,不等返回false System.out.println( "bd2.equals(bd3) = " + bd2.equals(bd3)); //false //compareTo方法比较两个BigDecimal对象的大小,相等返回0,小于返回-1,大于返回1。 System.out.println( "bd2测试数据pareTo(bd3) = " + bd2测试数据pareTo(bd3)); //0 //进行精确计算 System.out.println( "0.05 + 0.01 = " + BigNumber.add( 0.05 , 0.01 )); System.out.println( "1.0 - 0.42 = " + BigNumber.sub( 1.0 , 0.42 )); System.out.println( "4.015 * 100 =" + BigNumber.mul( 4.015 , 100 )); System.out.println( "123.3 / 100 = " + BigNumber.div( 123.3 , 100 )); } } |
(1)BigInteger和BigDecimal都是不可变(immutable)
在进行每一步运算时,都会产生一个新的对象,由于创建对象会引起开销,它们不适合于大量的数学计算,应尽量用long,float,double等基本类型做科学计算或者工程计算。
设计BigInteger和BigDecimal的目的是用来精确地表示大整数和小数,使用于在商业计算中使用。
(2)BigDecimal有4个够造方法
其中的两个用BigInteger构造,另一个是用double构造,还有一个使用String构造。
应该避免使用double构造BigDecimal,因为:有些数字用double根本无法精确表示,传给BigDecimal构造方法时就已经不精确了。比如,new BigDecimal(0.1)得到的值是0.1000000000000000055511151231257827021181583404541015625。
使用new BigDecimal("0.1")得到的值是0.1。因此,如果需要精确计算,用String构造BigDecimal,避免用double构造,尽管它看起来更简单!
(3)equals()方法认为0.1和0.1是相等的
返回true,而认为0.10和0.1是不等的,结果返回false。方法compareTo()则认为0.1与0.1相等,0.10与0.1也相等。所以在从数值上比较两个BigDecimal值时,应该使用compareTo()而不是 equals()。
(4)另外还有一些情形
任意精度的小数运算仍不能表示精确结果。例如,1除以9会产生无限循环的小数 .111111...。
出于这个原因,在进行除法运算时,BigDecimal可以让您显式地控制舍入。
运算结果:
0.060000000000000005
0.5800000000000001
401.49999999999994
1.2329999999999999
计算n的阶乘30! = 265252859812191058636308480000000
(bd1 = new BigDecimal(0.1)) = 0.1000000000000000055511151231257827021181583404541015625
(bd2 = new BigDecimal("0.1")) = 0.1
bd2.equals(bd3) = false
bd2测试数据pareTo(bd3) = 0
0.05 + 0.01 = 0.06
1.0 - 0.42 = 1.42
4.015 * 100 =104.015
123.3 / 100 = 223.3
java超长数据高精度计算(仅支持整数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
/** * Created by AndyJuseKing on 2020/1/2. * 超长数据高精度计算 * 仅支持整数 */ public class CYAccuracy { private static String cyNum; private static String nowNum; public CYAccuracy(String a){ cyNum = a; } public static void add(String n){ cyNum = makeAdd(cyNum,n); } public static String getAdd(String n){ nowNum = n; return makeAdd(cyNum,nowNum); } public static void subtract(String n){ nowNum = n; cyNum = makeSubtract(cyNum,nowNum); } public static String getSubtract(String n){ nowNum = n; return makeSubtract(cyNum,nowNum); } public static void multiply(String n){ nowNum = n; cyNum = makeMultiply(cyNum,nowNum); } public static String getMultiply(String n){ nowNum = n; return makeMultiply(cyNum,nowNum); } public static String[] divideAndRemainder(String n){ nowNum = n; String h = cyNum; h = removeZero(h); String i = h; String divNum = "" ; String remNum = "" ; String a = "0" ; int c = h.length(); int d = nowNum.length(); int e = c; while (d<=e){ String f = h; if (e==c){ f = h.substring( 0 , d); } String g = f; if (d<=c) { while (!f.contains( "-" )) { g = f; f = makeSubtract(f, n); a = makeAdd(a, "1" ); f = removeZero(f); } a = makeSubtract(a, "1" ); if (i.length()>=(d+divNum.length()+ 1 )) { h = addZero(g, 1 ); h = makeAdd(h, i.substring(d + divNum.length(), d + 1 + divNum.length())); } else { remNum = g; e = 0 ; } c = h.length(); divNum = divNum + a; a = "0" ; } else if (i.length()<(d+divNum.length()+ 1 )){ remNum = g; e = 0 ; } else { h = addZero(g, 1 ); h = makeAdd(h, i.substring(d+divNum.length(), d+ 1 +divNum.length())); c = h.length(); divNum = divNum + "0" ; } } // while (!newNum.contains("-")) { // newNum = makeSubtract(newNum,n); // a = makeAdd(a,"1"); // newNum = removeZero(newNum); // System.out.print(newNum + "\n"); // } // a = makeSubtract(a,"1"); // b = newNum.substring(1); return (divNum+ "," +remNum).split( "," ); } public static Double getDouble(){ return Double.parseDouble(cyNum); } public static Integer getInt(){ return Integer.parseInt(cyNum); } public static String getString(){ return cyNum; } private static String makeAdd(String x,String y){ String newNum = "" ; int i = 1 ; if (x.substring( 0 , 1 ).equals( "-" )){i = - 1 ;} int j = 1 ; if (y.substring( 0 , 1 ).equals( "-" )){j = - 1 ;} int m = x.length(); int n = y.length(); if (m < n) { int c = n - m; for ( int d = 0 ; d < c; d++) { x = "0" + x; } } else if (m > n) { int c = m - n; for ( int d = 0 ; d < c; d++) { y = "0" + y; } } String[] a = x.split( "" ); String[] b = y.split( "" ); int g = 0 ; for ( int c = a.length;c> 0 ;c--){ int d = c- 1 ; int f = (Integer.parseInt(a[d])*i) + (Integer.parseInt(b[d])*j) + g; int e = f% 10 ; newNum = e + newNum; g = f/ 10 ; if (d== 0 &&g!= 0 ){ newNum = g + newNum; } } return newNum; } private static String makeSubtract(String x,String y){ String newNum = "" ; int m = x.length(); int n = y.length(); if (m < n) { int c = n - m; for ( int d = 0 ; d < c; d++) { x = "0" + x; } } else if (m > n) { int c = m - n; for ( int d = 0 ; d < c; d++) { y = "0" + y; } } String[] a = x.split( "" ); String[] b = y.split( "" ); int g = 0 ; for ( int c = a.length;c> 0 ;c--){ int d = c- 1 ; int h = Integer.parseInt(a[d]); int i = Integer.parseInt(b[d]); int f = (h - i) + g; int e = f% 10 ; if (e==- 1 ){ e = 9 ; } g = f/ 10 ; if (e< 0 ){ g = g- 1 ; e = e * - 1 ; } newNum = e + newNum; if (d== 0 &&g< 0 ){ newNum = "-" + newNum; } } return newNum; } private static String makeMultiply(String x,String y){ String newNum = "0" ; String[] a = x.split( "" ); String[] b = y.split( "" ); String k = "" ; for ( int h = b.length;h> 0 ;h--) { int i = h - 1 ; k = k + "0" ; int g = 0 ; String j = "" ; for ( int c = a.length; c > 0 ; c--) { int d = c - 1 ; int f = (Integer.parseInt(a[d])+g) * Integer.parseInt(b[i]); int e = f % 10 ; j = e + j; g = f / 10 ; if (d == 0 && g != 0 ) { j = g + j; } } String l = j+k; newNum = makeAdd(newNum,l); } return newNum; } private static String removeZero(String x){ String y = x; String[] a = x.split( "" ); for ( int b = 0 ;b<a.length;b++){ if (y.substring( 0 , 1 ).equals( "0" )){ y = y.substring( 1 ); } } if ( "" .equals(y)){ y = "0" ; } return y; } private static String addZero(String x, int length){ while (length> 0 ){ x = x + "0" ; length--; } return x; } } |
以上为个人经验,希望能给大家一个参考,也希望大家多多支持。
原文链接:https://langyastudio.blog.csdn.net/article/details/45840149
查看更多关于Java 高精度的大数字运算方式的详细内容...