为神马 1.0 - 0.7 != 0.3 ???

直接上代码


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


为什么呢?

简单的说,问题处在"[color=red][b]IEEE 754 floating-point arithmetic[/b][/color]",虽然在java是遵循这个规则的,但是java语言的实现,并不是使用小数点或者十进制来表示数字,相反,它是采用分数和指数来表示,而且是[quote]uses binary fractions and exponents to represent[/quote]使用二进制的,我们可以举个例子:


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的实际结果却不是这样。

[size=large][b]更深入的话[/b][/size]

有人会问,为什么
System.out.println(0.1f);

输出的还是0.1呢?
因为在源代码里面println调用的是Float#toString(float),最终的实现是在

public static String toString(float f) {
return new FloatingDecimal(f).toJavaFormatString();
}

有兴趣的童鞋可以去阅读源代码,FloatingDecimal帮你做了很多事情。

这也牵涉出另外一个话题,如何避免上面出现的问题,

对的,就是[url=http://download.oracle.com/javase/6/docs/api/java/math/BigDecimal.html]BigDecimal[/url],关于BigDecimal,我相信你们在api上面会找到更多的答案。

[size=large][b]题外话[/b][/size]


BigDecimal(java.lang.String)
不要用
BigDecimal(double) or BigDecimal(float)

为什么呢?API上面写的很清楚了
[quote]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.
[/quote]
所以
        System.out.println(new BigDecimal(0.1f));
System.out.println(new BigDecimal("0.1"));
System.out.println(0.1f);

结果是不一样的

[size=large][b]更加更加更加深入的话[/b][/size]

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");


为何上面两串代码输出不一样?[b]为何0.3d就不能用3个0.1d相加,而0.2d就可以用2个0.1d相加呢[/b]?

原因的话,自己看下面的输出了

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)));

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值