当前位置:网站首页>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 :
BigDecimalThere are still many hidden pitsBigDecimalThe 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.331.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.00Don'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.52.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 !
边栏推荐
- previous permutation lintcode51
- Groovy test class and JUnit test
- P3250 [hnoi2016] Network + [necpc2022] f.tree path tree section + segment tree maintenance heap
- Nestjs配置服务,配置Cookie和Session
- Sheet1$.输出[Excel 源输出].列[XXX] 出错。返回的列状态是:“文本被截断,或者一个或多个字符在目标代码页中没有匹配项。”。
- Mmc5603nj geomagnetic sensor (Compass example)
- MCDF实验1
- 836. 合并集合(DAY 63)并查集
- 2022 northeast four provinces match VP record / supplementary questions
- Mysql根据时间搜索常用方法整理
猜你喜欢

ftp登录时,报错“530 Login incorrect.Login failed”

量化计算调研

OpenGL 索引缓存对象EBO和线宽模式

vulnhub之GeminiInc v2

The excel table is transferred to word, and the table does not exceed the edge paper range

Software testing weekly (issue 78): the more confident you are about the future, the more patient you are about the present.

Raven2 of vulnhub

Mmc5603nj geomagnetic sensor (Compass example)

STL教程10-容器共性和使用场景

Understand go language context in one article
随机推荐
ORACLE进阶(一) 通过EXPDP IMPDP命令实现导dmp
Excel表格转到Word中,表格不超边缘纸张范围
解决msvcp120d.dll和msvcr120d.dll缺失
牛牛的组队竞赛
previous permutation lintcode51
836. Merge sets (day 63) and search sets
libvirt 中体验容器
Master and backup role election strategy in kept
(database authorization - redis) summary of unauthorized access vulnerabilities in redis
Hongmeng fourth training
Solution to the second weekly test of ACM intensive training of Hunan Institute of technology in 2022
mysql使用update联表更新的方法
vulnhub之GeminiInc
PHP Basics
Visual studio 2022 downloading and configuring opencv4.5.5
Ripper of vulnhub
DS90UB949
Some common terms
OpenStack中的测试分类
Vulnhub's Tomato (tomato)