一天一块钱第二天翻倍
很久以前,我写了一篇关于用双倍赚钱的文章。 但是,当解决方案相当简单时,仍然是许多开发人员普遍担心的问题。
用双倍赚钱的问题
double有两种类型的错误。 它存在表示错误。 即它不能精确地表示所有可能的十进制值。 即使0.1也不完全是这个值。 根据计算也有舍入误差。 即,当您执行计算时,误差会增加。
double[] ds = {0.1,0.2,-0.3,0.1 + 0.2 - 0.3};for (double d : ds) {System.out.println(d + " => " + new BigDecimal(d));
}
版画
0.1 => 0.1000000000000000055511151231257827021181583404541015625
0.2 => 0.200000000000000011102230246251565404236316680908203125
-0.3 => -0.299999999999999988897769753748434595763683319091796875
5.551115123125783E-17 => 5.5511151231257827021181583404541015625E-17
您可以看到0.1和0.2的表示形式略高于这些值,-0.3的表示形式也略高。 当您打印它们时,您得到的更好的是0.1,而不是代表的实际值0.1000000000000000055511151231257827021181583404541015625
但是,将这些值加在一起时,会得到一个比0稍高的值。
要记住的重要一点是这些错误不是随机错误。 它们是可管理的且受限制的。
修正舍入误差
像许多数据类型(例如日期)一样,您具有值的内部表示形式以及如何将其表示为字符串。
对于double来说是这样。 您需要控制如何将值表示为字符串。 由于Java对表示错误进行少量舍入并不明显,所以这可能会令人惊讶,但是,一旦对操作也产生舍入错误,就会有些震惊。
一个常见的React是假设,您对此无能为力,该错误是不可控制的,不可知的且危险的。 放弃双倍并使用BigDecimal
但是,该错误在IEE-754标准中受到限制,并且累积缓慢。
舍入结果
就像需要使用TimeZone和Local作为日期一样,您需要在转换为String之前确定结果的精度。
要解决此问题,您需要提供适当的舍入。 有了钱,这很容易,因为您知道合适的小数位数,除非您有70万亿美元,否则舍入误差不会大到无法校正的程度。
// uses round half up, or bankers' roundingpublic static double roundToTwoPlaces(double d) {return Math.round(d * 100) / 100.0;
}// ORpublic static double roundToTwoPlaces(double d) {return ((long) (d < 0 ? d * 100 - 0.5 : d * 100 + 0.5)) / 100.0;
}
如果将其添加到结果中,仍然会出现一个很小的表示错误,但是它不够大,以致Double.toString(d)无法对其进行校正。
double[] ds = {0.1,0.2,-0.3,0.1 + 0.2 - 0.3};for (double d : ds) {System.out.println(d + " to two places " + roundToTwoPlaces(d) + " => " + new BigDecimal(roundToTwoPlaces(d)));
}
版画
0.1 to two places 0.1 => 0.1000000000000000055511151231257827021181583404541015625
0.2 to two places 0.2 => 0.200000000000000011102230246251565404236316680908203125
-0.3 to two places -0.3 => -0.299999999999999988897769753748434595763683319091796875
5.551115123125783E-17 to two places 0.0 => 0
结论
如果您有一个项目标准说应该使用BigDecimal或double,则应遵循该标准。 但是,没有充分的技术理由担心会花双倍的钱。
参考:在Vanilla Java上 ,与我们的JCG合作伙伴 Peter Lawrey 再次将钱 翻倍 。
- Java中的低GC:使用原语而不是包装器
- Java Lambda语法替代
- JVM如何处理锁
- Erlang与Java内存架构
- Java Fork / Join进行并行编程
- Java最佳实践系列
- 如何在Java中获得类似于C的性能
翻译自: https://www.javacodegeeks.com/2011/08/double-your-money-again.html
一天一块钱第二天翻倍