陷阱:浮点数遵循IEEE754标准,无法精确保存0.1
问题
- 看三行简单却诡异的代码,观察运行结果
double x = 1.0 / 10;
double y = 1 - 0.9;
double z = 0.1+0.2;
// 观察x、y、z的结果
System.out.println(x);
System.out.println(y);
System.out.println(z);
- 为什么1 - 0.9 != 0.1 ?
- 为什么0.1+0.2 != 0.3 ?
结论
在计算机中,浮点数虽然表示的范围大,但是浮点数有个非常重要的特点——浮点数常常无法精确表示。
例如,浮点数0.1在计算机中就无法精确表示,所以运算结果就会出现偏差。
解析
如果你了解浮点数二进制存储机制,那么就很简单,二进制只能精确存储小数域是2的负指数幂及其线性组合的数。例如0.25、0.5…
如果你不了解,那么一起来看…
0.1 = 1/24 + 1/25 + 1/28 +1/29+1/212+1/213…
0.110=0.0001100110011001100110011…2
这是一个二进制无限循环小数。
计算机内存有限,我们不能储存所有的小数位数,所以在某个精度点直接舍弃,单精度精确到23位,双精度精确到52位。
也就是说,0.1在计算机内部根本就不是精确的0.1,而是一个有舍入误差的0.1。当代码被编译或解释后,0.1已经被四舍五入成一个与之很接近的计算机内部数字,导致计算还没开始,一个很小的舍入错误就已经产生了。这也就是上面问题产生的根本原因。
关于浮点数的存储机制请移步——浮点数揭秘——IEEE754
结语
不同行业,要求的精度不是线性的,我们允许(对结果无关紧要的)误差存在,例如10.0001与10.001在铁路工程师看来都是合格的。
虽然允许误差存在,但是程序员在使用浮点数进行计算或逻辑处理时,稍不注意,就可能会问题。
记住,最好完全避免使用浮点数进行比较!
在银行业务中用Java中的BigDecimal数学工具类来表示钱数。
水平有限,如有错误之处,还请指正。