当前位置:网站首页>Is BigDecimal safe to calculate the amount? Look at these five pits~~
Is BigDecimal safe to calculate the amount? Look at these five pits~~
2022-07-03 11:55:00 【Java confidant_】
I saw an article last week because it was not used in the amount calculation BigDecimal
And the article that caused the failure , But unless in some very simple scenes , Settlement of remittance businesses will not be directly used BigDecimal
To calculate the amount , For two reasons :
BigDecimal
There are still many hidden pitsBigDecimal
The unit that did not provide the amount
1. BigDecimal
Five easy pits in
1.1 new BigDecimal()
still BigDecimal#valueOf()
?
First look at the following code
BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = BigDecimal.valueOf(0.01);
System.out.println("bd1 = " + bd1);
System.out.println("bd2 = " + bd2);
The output to the console is :
bd1 = 0.01000000000000000020816681711721685132943093776702880859375
bd2 = 0.01
The reason for this difference is 0.1 This digital computer cannot accurately express , Send BigDecimal
The accuracy has been lost by , and BigDecimal#valueOf
The implementation of is completely different
public static BigDecimal valueOf(double val) {
// Reminder: a zero double returns '0.0', so we cannot fastpath
// to use the constant ZERO. This might be important enough to
// justify a factory approach, a cache, or a few private
// constants, later.
return new BigDecimal(Double.toString(val));
}
It uses the corresponding string of floating-point numbers to construct BigDecimal
object , Therefore, the accuracy problem is avoided . So we should try to use strings instead of floating-point numbers to construct BigDecimal
object , If it doesn't work , Just use BigDecimal#valueOf()
Methods! .
1.2 Equivalence comparison
BigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("1.00");
System.out.println(bd1.equals(bd2));
System.out.println(bd1.compareTo(bd2));
The output of the console will be :
false
0
The reason is ,BigDecimal
in equals
The implementation of the method will compare the accuracy of two numbers , and compareTo
Method will only compare the size of the value .
1.3 BigDecimal
Does not mean infinite accuracy
First look at this code
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("3.0");
a.divide(b) // results in the following exception.
The result is an exception :
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
About this exception ,Oracle The official documents of have specific instructions
If the quotient has a nonterminating decimal expansion and the operation is specified to return an exact result, an ArithmeticException is thrown. Otherwise, the exact result of the division is returned, as done for other operations.
The main idea is , If the result of the quotient of division is an infinite decimal, but we expect to return the exact result , Then the program will throw an exception . Back to our example , We need to tell JVM
We don't need to return exact results
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("3.0");
a.divide(b, 2, RoundingMode.HALF_UP)// 0.33
1.4 BigDecimal
Turn it back String
Be careful
BigDecimal d = BigDecimal.valueOf(12334535345456700.12345634534534578901);
String out = d.toString(); // Or perform any formatting that needs to be done
System.out.println(out); // 1.23345353454567E+16
It can be seen that the results have been converted into scientific counting , Maybe this is not the expected result BigDecimal
There are three methods that can be converted to the corresponding string type , Remember not to use the wrong :
String toString(); // Use scientific counting when necessary
String toPlainString(); // No scientific counting
String toEngineeringString(); // The method of recording numbers often used in engineering calculation , Similar to scientific counting , But ask for 10 The power of must be 3 Multiple
1.5 The execution sequence cannot be changed ( The commutative law of multiplication fails )
It is common sense that multiplication satisfies the commutative law , But in the world of computers , There will be situations that do not satisfy the commutative law of multiplication
BigDecimal a = BigDecimal.valueOf(1.0);
BigDecimal b = BigDecimal.valueOf(3.0);
BigDecimal c = BigDecimal.valueOf(3.0);
System.out.println(a.divide(b, 2, RoundingMode.HALF_UP).multiply(c)); // 0.990
System.out.println(a.multiply(c).divide(b, 2, RoundingMode.HALF_UP)); // 1.00
Don't underestimate this 0.01 The difference between , In Huijin , There will be a very large amount difference .
2. Best practices
About the amount calculation , Many business teams will be based on BigDecimal
One more Money
class , In fact, we can directly use a semi official Money
class :JSR 354 , Although not in Java 9
To become Java
standard , It is likely to be integrated into subsequent Java
Become the official library in version .
2.1 maven
coordinate
<dependency>
<groupId>org.javamoney</groupId>
<artifactId>moneta</artifactId>
<version>1.1</version>
</dependency>
2.2 newly build Money
class
CurrencyUnit cny = Monetary.getCurrency("CNY");
Money money = Money.of(1.0, cny);
// perhaps Money money = Money.of(1.0, "CNY");
//System.out.println(money);
2.3 Amount calculation
CurrencyUnit cny = Monetary.getCurrency("CNY");
Money oneYuan = Money.of(1.0, cny);
Money threeYuan = oneYuan.add(Money.of(2.0, "CNY")); //CNY 3
Money tenYuan = oneYuan.multiply(10); // CNY 10
Money fiveFen = oneYuan.divide(2); //CNY 0.5
2.4 More equal
Money fiveFen = Money.of(0.5, "CNY"); //CNY 0.5
Money anotherFiveFen = Money.of(0.50, "CNY"); // CNY 0.50
System.out.println(fiveFen.equals(anotherFiveFen)); // true
You can see , This class abstracts the amount explicitly , Increase the unit of the amount , It also avoids direct use BigDecimal
Some pits .
recommend
Technical involution group , Learn together !!
PS: Because the official account platform changed the push rules. , If you don't want to miss the content , Remember to click after reading “ Looking at ”, Add one “ Star standard ”, In this way, each new article push will appear in your subscription list for the first time . spot “ Looking at ” Support us !
边栏推荐
猜你喜欢
鸿蒙第三次培训(项目实训)
Ripper of vulnhub
STL教程10-容器共性和使用场景
The uniapp scroll view solves the problems of high adaptability and bullet frame rolling penetration.
STL教程9-容器元素深拷贝和浅拷贝问题
Mmc5603nj geomagnetic sensor (Compass example)
AI模型看看视频,就学会了玩《我的世界》:砍树、造箱子、制作石镐样样不差...
Introduction to the implementation principle of rxjs observable filter operator
Web security summary
外插散点数据
随机推荐
Capturing and sorting out external Fiddler -- Conversation bar and filter [2]
鸿蒙第四次培训
Modular programming of single chip microcomputer
ArcGIS应用(二十一)Arcmap删除图层指定要素的方法
R语言使用gridExtra包的grid.arrange函数将ggplot2包的多个可视化图像横向组合起来,ncol参数自定义组合图列数、nrow参数自定义组合图行数
ArcGIS application (XXI) ArcMap method of deleting layer specified features
previous permutation lintcode51
MCDF Experiment 1
牛牛的组队竞赛
Vulnhub geminiinc
Unity3d learning notes 5 - create sub mesh
previous permutation lintcode51
MCDF实验1
Software testing weekly (issue 78): the more confident you are about the future, the more patient you are about the present.
Solution to the second weekly test of ACM intensive training of Hunan Institute of technology in 2022
The excel table is transferred to word, and the table does not exceed the edge paper range
Slam mapping and autonomous navigation simulation based on turnlebot3
GCC compilation process and dynamic link library and static link library
Hongmeng fourth training
Xml的(DTD,xml解析,xml建模)