Java 高精度的大數(shù)字運算
為了解決Java基本數(shù)據(jù)類型在運算時會出現(xiàn)的溢出和計算不精確的問題。Java 提供了兩個類BigInteger和BigDecimal,專門用于進行高精度運算。凡是能用int 或float 做的事情,用BigInteger和BigDecimal也可以做,只是必須換用方法調(diào)用,而不是使用運算符。
高精度整數(shù)BigInteger
BigInteger支持任意精度的整數(shù),也就是說我們可精確表示任意大小的整數(shù)值;同時在運算過程中不會丟失任何信息;
高精度浮點數(shù)BigDecimal
它可以表示任意精度的小數(shù),并對它們進行計算。由于 BigDecimal 對象是不可變的,這些方法中的每一個都會產(chǎn)生新的 BigDecimal 對象。因此,因為創(chuàng)建對象的開銷,BigDecimal 不適合于大量的數(shù)學計算,但設計它的目的是用來精確地表示小數(shù)。
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 { //默認除法運算精度,即保留小數(shù)點多少位 private static final int DEFAULT_DIV_SCALE = 10 ; //這個類不能實例化 private BigNumber() { } /** * 提供精確的加法運算。 * @param v1 被加數(shù) * @param v2 加數(shù) * @return 兩個參數(shù)的和 */ 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 被減數(shù) * @param v2 減數(shù) * @return 兩個參數(shù)的差 */ 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 被乘數(shù) * @param v2 乘數(shù) * @return 兩個參數(shù)的積 */ 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(); } /** * 提供(相對)精確的除法運算,當發(fā)生除不盡的情況時,精確到 * 小數(shù)點以后多少位,以后的數(shù)字四舍五入。 * @param v1 被除數(shù) * @param v2 除數(shù) * @return 兩個參數(shù)的商 */ public static double div( double v1, double v2) { return div(v1, v2, DEFAULT_DIV_SCALE); } /** * 提供(相對)精確的除法運算。當發(fā)生除不盡的情況時,由scale參數(shù)指 * 定精度,以后的數(shù)字四舍五入。 * @param v1 被除數(shù) * @param v2 除數(shù) * @param scale 表示需要精確到小數(shù)點以后幾位。 * @return 兩個參數(shù)的商 */ 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" ); } //將數(shù)組換成字符串后構(gòu)造BigInteger BigInteger result = new BigInteger( "1" ); for (; n > 0 ; n--) { //將數(shù)字n轉(zhuǎn)換成字符串后,再構(gòu)造一個BigInteger對象,與現(xiàn)有結(jié)果做乘法 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構(gòu)造BigDecimal BigDecimal bd1 = new BigDecimal( 0.1 ); System.out.println( "(bd1 = new BigDecimal(0.1)) = " + bd1.toString()); //用String構(gòu)造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.compareTo(bd3) = " + bd2.compareTo(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)
在進行每一步運算時,都會產(chǎn)生一個新的對象,由于創(chuàng)建對象會引起開銷,它們不適合于大量的數(shù)學計算,應盡量用long,float,double等基本類型做科學計算或者工程計算。
設計BigInteger和BigDecimal的目的是用來精確地表示大整數(shù)和小數(shù),使用于在商業(yè)計算中使用。
(2)BigDecimal有4個夠造方法
其中的兩個用BigInteger構(gòu)造,另一個是用double構(gòu)造,還有一個使用String構(gòu)造。
應該避免使用double構(gòu)造BigDecimal,因為:有些數(shù)字用double根本無法精確表示,傳給BigDecimal構(gòu)造方法時就已經(jīng)不精確了。比如,new BigDecimal(0.1)得到的值是0.1000000000000000055511151231257827021181583404541015625。
使用new BigDecimal("0.1")得到的值是0.1。因此,如果需要精確計算,用String構(gòu)造BigDecimal,避免用double構(gòu)造,盡管它看起來更簡單!
(3)equals()方法認為0.1和0.1是相等的
返回true,而認為0.10和0.1是不等的,結(jié)果返回false。方法compareTo()則認為0.1與0.1相等,0.10與0.1也相等。所以在從數(shù)值上比較兩個BigDecimal值時,應該使用compareTo()而不是 equals()。
(4)另外還有一些情形
任意精度的小數(shù)運算仍不能表示精確結(jié)果。例如,1除以9會產(chǎn)生無限循環(huán)的小數(shù) .111111...。
出于這個原因,在進行除法運算時,BigDecimal可以讓您顯式地控制舍入。
運算結(jié)果:
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.compareTo(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超長數(shù)據(jù)高精度計算(僅支持整數(shù))
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. * 超長數(shù)據(jù)高精度計算 * 僅支持整數(shù) */ 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; } } |
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持服務器之家。
原文鏈接:https://langyastudio.blog.csdn.net/article/details/45840149