09 数值计算：注意精度、舍入和溢出问题 今天，我要和你说说数值计算的精度、舍入和溢出问题。

## “危险”的 Double

0.30000000000000004 0.19999999999999996 401.49999999999994 1.2329999999999999

0.3000000000000000166533453693773481063544750213623046875 0.1999999999999999555910790149937383830547332763671875 401.49999999999996802557689079549163579940795898437500 1.232999999999999971578290569595992565155029296875

System.out.println(new BigDecimal(“0.1”).add(new BigDecimal(“0.2”))); System.out.println(new BigDecimal(“1.0”).subtract(new BigDecimal(“0.8”))); System.out.println(new BigDecimal(“4.015”).multiply(new BigDecimal(“100”))); System.out.println(new BigDecimal(“123.3”).divide(new BigDecimal(“100”)));

0.3 0.2 401.500 1.233

BigDecimal 的 toString 方法得到的字符串和 scale 相关，又会引出了另一个问题：对于浮点数的字符串形式输出和格式化，我们应该考虑显式进行，通过格式化表达式或格式化工具来明确小数位数和舍入方式。接下来，我们就聊聊浮点数舍入和格式化。

## 考虑浮点数舍入和格式化的方式

String.format 采用四舍五入的方式进行舍入，取 1 位小数，double 的 3.350 四舍五入为 3.4，而 float 的 3.349 四舍五入为 3.3。

double num1 = 3.35; float num2 = 3.35f; DecimalFormat format = new DecimalFormat(“/#./#/#”); format.setRoundingMode(RoundingMode.DOWN); System.out.println(format.format(num1)); format.setRoundingMode(RoundingMode.DOWN); System.out.println(format.format(num2));

## 用 equals 做判等，就一定是对的吗？

/// /* Compares this {@code BigDecimal} with the specified /* {@code Object} for equality. Unlike {@link /* /#compareTo(BigDecimal) compareTo}, this method considers two /* {@code BigDecimal} objects equal only if they are equal in /* value and scale (thus 2.0 is not equal to 2.00 when compared by /* this method). /* /* @param x {@code Object} to which this {@code BigDecimal} is /* to be compared. /* @return {@code true} if and only if the specified {@code Object} is a /* {@code BigDecimal} whose value and scale are equal to this /* {@code BigDecimal}’s. /* @see /#compareTo(java.math.BigDecimal) /* @see /#hashCode /*/ @Override public boolean equals(Object x)

System.out.println(new BigDecimal(“1.0”).compareTo(new BigDecimal(“1”))==0);

Set hashSet1 = new HashSet<>(); hashSet1.add(new BigDecimal("1.0")); System.out.println(hashSet1.contains(new BigDecimal("1")));//返回false

Set hashSet2 = new HashSet<>(); hashSet2.add(new BigDecimal("1.0").stripTrailingZeros()); System.out.println(hashSet2.contains(new BigDecimal("1.000").stripTrailingZeros()));//返回true

## 小心数值溢出问题

-9223372036854775808 true

java.lang.ArithmeticException: long overflow at java.lang.Math.addExact(Math.java:809) at org.geekbang.time.commonmistakes.numeralcalculations.demo3.CommonMistakesApplication.right2(CommonMistakesApplication.java:25) at org.geekbang.time.commonmistakes.numeralcalculations.demo3.CommonMistakesApplication.main(CommonMistakesApplication.java:13)

9223372036854775808 java.lang.ArithmeticException: BigInteger out of long range at java.math.BigInteger.longValueExact(BigInteger.java:4632) at org.geekbang.time.commonmistakes.numeralcalculations.demo3.CommonMistakesApplication.right1(CommonMistakesApplication.java:37) at org.geekbang.time.commonmistakes.numeralcalculations.demo3.CommonMistakesApplication.main(CommonMistakesApplication.java:11)

## 思考与讨论

BigDecimal提供了 8 种舍入模式，你能通过一些例子说说它们的区别吗？

# 参考资料

https://learn.lianglianglee.com/%e4%b8%93%e6%a0%8f/Java%20%e4%b8%9a%e5%8a%a1%e5%bc%80%e5%8f%91%e5%b8%b8%e8%a7%81%e9%94%99%e8%af%af%20100%20%e4%be%8b/09%20%e6%95%b0%e5%80%bc%e8%ae%a1%e7%ae%97%ef%bc%9a%e6%b3%a8%e6%84%8f%e7%b2%be%e5%ba%a6%e3%80%81%e8%88%8d%e5%85%a5%e5%92%8c%e6%ba%a2%e5%87%ba%e9%97%ae%e9%a2%98.md