该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2011-01-21
最后修改:2011-01-21
public static void main(String[] args) { System.out.println(1.0 - 0.1); System.out.println(1.0 - 0.2); System.out.println(1.0 - 0.3); System.out.println(1.0 - 0.4); System.out.println(1.0 - 0.5); System.out.println(1.0 - 0.6); System.out.println(1.0 - 0.7); System.out.println(1.0 - 0.8); System.out.println(1.0 - 0.9); System.out.println(1.0 - 1.0); } 最后输出的结果为神马是 0.9 0.8 0.7 0.6 0.5 0.4 0.30000000000000004 0.19999999999999996 0.09999999999999998 0.0 为什么呢? 简单的说,问题处在"IEEE 754 floating-point arithmetic",虽然在java是遵循这个规则的,但是java语言的实现,并不是使用小数点或者十进制来表示数字,相反,它是采用分数和指数来表示,而且是 引用 uses binary fractions and exponents to represent 使用二进制的,我们可以举个例子:
0.5 = 1/2 0.75 = 1/2 + 1/(2^2) 0.85 = 1/2 + 1/(2^2) + 1/(2^3) 0.1 = 1/(2^4) + 1/(2^5) + 1/(2^8) + ... 注意,0.1只能是无限循环下去的,这就意味着0.1在java里面不能够准确的用浮点数来表示,也就造成了浮点数运算上面的误差。 举个例子: if (0.1 + 0.1 + 0.1 != 0.3) System.out.println("0.1 + 0.1 + 0.1 is not equal with 0.3"); else System.out.println("0.1 + 0.1 + 0.1 is equal to 0.3"); 每个人都知道,0.1 + 0.1 + 0.1 == 0.3,但是在java的实际结果却不是这样。 更深入的话 有人会问,为什么 System.out.println(0.1f); 输出的还是0.1呢? 因为在源代码里面println调用的是Float#toString(float),最终的实现是在 public static String toString(float f) { return new FloatingDecimal(f).toJavaFormatString(); } 有兴趣的童鞋可以去阅读源代码,FloatingDecimal帮你做了很多事情。 这也牵涉出另外一个话题,如何避免上面出现的问题, 对的,就是BigDecimal,关于BigDecimal,我相信你们在api上面会找到更多的答案。 题外话 用 BigDecimal(java.lang.String) 不要用 BigDecimal(double) or BigDecimal(float) 为什么呢?API上面写的很清楚了 引用 The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.
所以 System.out.println(new BigDecimal(0.1f)); System.out.println(new BigDecimal("0.1")); System.out.println(0.1f); 结果是不一样的 更加更加更加深入的话 if (0.1f + 0.1f + 0.1f != 0.3f) System.out.println("0.1 + 0.1 + 0.1 is not equal to 0.3"); else System.out.println("0.1 + 0.1 + 0.1 is equal to 0.3"); if (0.1 + 0.1 != 0.2) System.out.println("0.1 + 0.1 is not equal to 0.2"); else System.out.println("0.1 + 0.1 is equal to 0.2"); 为何上面两串代码输出不一样?为何0.3d就不能用3个0.1d相加,而0.2d就可以用2个0.1d相加呢? 原因的话,自己看下面的输出了 System.out.println(new BigDecimal(0.2d)); System.out.println(new BigDecimal(0.1d).add(new BigDecimal(0.1d))); System.out.println(new BigDecimal(0.2f)); System.out.println(new BigDecimal(0.1f).add(new BigDecimal(0.1f))); System.out.println(new BigDecimal(0.3d)); System.out.println(new BigDecimal(0.1d).add(new BigDecimal(0.1d)).add(new BigDecimal(0.1d))); System.out.println(new BigDecimal(0.3f)); System.out.println(new BigDecimal(0.1f).add(new BigDecimal(0.1f)).add(new BigDecimal(0.1f))); 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-01-22
如果能接受10进制数不能准确表示1/3的话,2进制数字不能表示3/10那应该不难理解呀。
|
|
返回顶楼 | |
发表时间:2011-01-22
其实这就是一个F&Q,时常被问的问题,所以干脆直接提出来写一篇好了,查阅了不少资料,自己收获也不少
|
|
返回顶楼 | |
发表时间:2011-01-22
Java Puzzlers里有
|
|
返回顶楼 | |
发表时间:2011-01-22
IEEE浮点数格式: 根据不同的性能和精度需求,其实现了三种浮点数格式: 1.单精度 single precision:32位。 其中尾数23位,第23位(从0位起算)永远为一,8位阶码(移码表示,余-127格式),第31位为符号位。 2.双精度 double precision:64位。 其中尾数52位,第52位(从0位起算)永远为一,11位阶码(移码表示,余-1023格式),第63位为符号位。 3.扩展精度 extended precision:80位。 额外的16位中,12位用于尾数,4位用于阶码。它尾数中不包含总为一的隐含位。所以扩展精度格式,64位尾数,15位余-16383阶码,1位符号位。 特殊的浮点数: 1.阶码全1,尾数非0: NaN (Not a Number)。 2.阶码全1,尾数全0: 正或负的无穷大。 3.阶码全0: -0或+0。 BCD,binary-coded decimal,即二进制编码的十进制格式。它由半个字节二进制表示一个十进制数字。 因此,BCD的存储效率不高,一个八位的BCD变量表示数的范围为0~99。而同样八位的二进制数,表数范围为0~255。 使用二进制定点记数,对大多数实数只能是近似表示(这一点应在编程中操作定点数时注意)。但是这并不意味着使用BCD会比二进制格式更加精确-因为上面的 BCD存储效率问题。例如,使用八位表示小数,BCD能表示100个不同的数值,而二进制能表示256个不同的小数值。所以,一般来说,二进制格式将更精确些。 |
|
返回顶楼 | |
发表时间:2011-01-22
呵 最好用这个 BigDecimal(String val)
神马都是骡子。。。。 |
|
返回顶楼 | |
发表时间:2011-01-22
之前被问到,涉及比较抽象。故当时答得很烂。真后悔没有好好去研究
|
|
返回顶楼 | |
发表时间:2011-01-23
可能真是语言实现机制不一样。写了个c++的程序(TRACE函数来自mfc)
TRACE("%f\n",1.0 - 0.1); TRACE("%f\n",1.0 - 0.2); TRACE("%f\n",1.0 - 0.3); TRACE("%f\n",1.0 - 0.4); TRACE("%f\n",1.0 - 0.5); TRACE("%f\n",1.0 - 0.6); TRACE("%f\n",1.0 - 0.7); TRACE("%f\n",1.0 - 0.8); TRACE("%f\n",1.0 - 0.9); TRACE("%f\n",1.0 - 1.0); 输出: 0.900000 0.800000 0.700000 0.600000 0.500000 0.400000 0.300000 0.200000 0.100000 0.000000 |
|
返回顶楼 | |
发表时间:2011-01-23
受教了。
之前一直只知道浮点数不精确,但是不知道原因。 |
|
返回顶楼 | |
发表时间:2011-01-23
浮点数不是这样比较的
|
|
返回顶楼 | |