用了BigDecimal就不會(huì)資損?了解下BigDecimal這五個(gè)坑
往期熱門文章:
1、一個(gè)依賴搞定 Spring Boot 反爬蟲,防止接口盜刷!
BigDecimal而導(dǎo)致故障的文章,但是除非在一些非常簡單的場(chǎng)景,結(jié)算匯金類的業(yè)務(wù)也不會(huì)直接用BigDecimal來計(jì)算金額,原因有兩點(diǎn):BigDecimal里面還是有很多隱蔽的坑的BigDecimal沒有提供金額的單位
1. BigDecimal中的五個(gè)容易踩的坑
1.1 new BigDecimal()還是BigDecimal#valueOf()?
BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = BigDecimal.valueOf(0.01);
System.out.println("bd1 = " + bd1);
System.out.println("bd2 = " + bd2);
bd1 = 0.01000000000000000020816681711721685132943093776702880859375
bd2 = 0.01
BigDecimal的時(shí)候就已經(jīng)丟精度了,而BigDecimal#valueOf的實(shí)現(xiàn)卻完全不同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));
}
BigDecimal對(duì)象,因此避免了精度問題。所以大家要盡量要使用字符串而不是浮點(diǎn)數(shù)去構(gòu)造BigDecimal對(duì)象,如果實(shí)在不行,就使用BigDecimal#valueOf()方法吧。1.2 等值比較
BigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("1.00");
System.out.println(bd1.equals(bd2));
System.out.println(bd1.compareTo(bd2));
false
0
BigDecimal中equals方法的實(shí)現(xiàn)會(huì)比較兩個(gè)數(shù)字的精度,而compareTo方法則只會(huì)比較數(shù)值的大小。1.3 BigDecimal并不代表無限精度
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("3.0");
a.divide(b) // results in the following exception.
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
JVM我們不需要返回精確的結(jié)果就好了BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("3.0");
a.divide(b, 2, RoundingMode.HALF_UP)// 0.33
1.4 BigDecimal轉(zhuǎn)回String要小心
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
BigDecimal有三個(gè)方法可以轉(zhuǎn)為相應(yīng)的字符串類型,切記不要用錯(cuò):String toString(); // 有必要時(shí)使用科學(xué)計(jì)數(shù)法
String toPlainString(); // 不使用科學(xué)計(jì)數(shù)法
String toEngineeringString(); // 工程計(jì)算中經(jīng)常使用的記錄數(shù)字的方法,與科學(xué)計(jì)數(shù)法類似,但要求10的冪必須是3的倍數(shù)
1.5 執(zhí)行順序不能調(diào)換(乘法交換律失效)
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
2. 最佳實(shí)踐
BigDecimal再封裝一個(gè)Money類,其實(shí)我們直接可以用一個(gè)半官方的Money類:JSR 354 ,雖然沒能在Java 9中成為Java標(biāo)準(zhǔn),很有可能集成到后續(xù)的Java版本中成為官方庫。2.1 maven坐標(biāo)
<dependency>
<groupId>org.javamoney</groupId>
<artifactId>moneta</artifactId>
<version>1.1</version>
</dependency>
2.2 新建Money類
CurrencyUnit cny = Monetary.getCurrency("CNY");
Money money = Money.of(1.0, cny);
// 或者 Money money = Money.of(1.0, "CNY");
//System.out.println(money);
2.3 金額運(yùn)算
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 比較相等
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
BigDecimal的一些坑。往期熱門文章:
1、線上MySQL的自增id用盡怎么辦?被面試官干趴下了! 2、計(jì)算機(jī)專業(yè)會(huì)不會(huì)成為下一個(gè)土木? 3、xxl-job驚艷的設(shè)計(jì),怎能叫人不愛 4、ArrayList#subList這四個(gè)坑,一不小心就中招 5、面試官:大量請(qǐng)求 Redis 不存在的數(shù)據(jù),從而影響數(shù)據(jù)庫,該如何解決? 6、MySQL 暴跌! 7、超越 Xshell!號(hào)稱下一代 Terminal 終端神器,用完愛不釋手! 8、IDEA 官宣全新默認(rèn) UI,太震撼了!! 9、讓你直呼「臥槽」的 GitHub 項(xiàng)目! 10、Kafka又笨又重,為啥不選Redis?
評(píng)論
圖片
表情
