Java中容易犯错的一个地方(转)

来源:互联网 发布:php会员管理系统模板 编辑:程序博客网 时间:2024/06/08 01:10

直接上代码

Java代码 复制代码 收藏代码Java中容易犯错的一个地方(转) - 空想主义者 - 空想主义者的博客
  1. public static void main(String[] args) {   
  2.     System.out.println(1.0 - 0.1);   
  3.     System.out.println(1.0 - 0.2);   
  4.     System.out.println(1.0 - 0.3);   
  5.     System.out.println(1.0 - 0.4);   
  6.     System.out.println(1.0 - 0.5);   
  7.     System.out.println(1.0 - 0.6);   
  8.     System.out.println(1.0 - 0.7);   
  9.     System.out.println(1.0 - 0.8);   
  10.     System.out.println(1.0 - 0.9);   
  11.     System.out.println(1.0 - 1.0);   
  12. }  
    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);      }  



最后输出的结果为神马是

Java代码 复制代码 收藏代码Java中容易犯错的一个地方(转) - 空想主义者 - 空想主义者的博客
  1. 0.9  
  2. 0.8  
  3. 0.7  
  4. 0.6  
  5. 0.5  
  6. 0.4  
  7. 0.30000000000000004  
  8. 0.19999999999999996  
  9. 0.09999999999999998  
  10. 0.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

使用二进制的,我们可以举个例子:

Java代码 复制代码 收藏代码Java中容易犯错的一个地方(转) - 空想主义者 - 空想主义者的博客
  1. 0.5 = 1/2  
  2. 0.75 = 1/2 + 1/(2^2)   
  3. 0.85 = 1/2 + 1/(2^2) + 1/(2^3)   
  4. 0.1 = 1/(2^4) + 1/(2^5) + 1/(2^8) + ...  
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里面不能够准确的用浮点数来表示,也就造成了浮点数运算上面的误差。

举个例子:

Java代码 复制代码 收藏代码Java中容易犯错的一个地方(转) - 空想主义者 - 空想主义者的博客
  1. if (0.1 + 0.1 + 0.1 != 0.3)   
  2.     System.out.println("0.1 + 0.1 + 0.1 is not equal with 0.3");   
  3. else  
  4.     System.out.println("0.1 + 0.1 + 0.1 is equal to 0.3");  
        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的实际结果却不是这样。

更深入的话

有人会问,为什么

Java代码 复制代码 收藏代码Java中容易犯错的一个地方(转) - 空想主义者 - 空想主义者的博客
  1. System.out.println(0.1f);  
System.out.println(0.1f);


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

Java代码 复制代码 收藏代码Java中容易犯错的一个地方(转) - 空想主义者 - 空想主义者的博客
  1. public static String toString(float f) {   
  2.     return new FloatingDecimal(f).toJavaFormatString();   
  3. }  
    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.


所以

Java代码 复制代码 收藏代码Java中容易犯错的一个地方(转) - 空想主义者 - 空想主义者的博客
  1. System.out.println(new BigDecimal(0.1f));   
  2. System.out.println(new BigDecimal("0.1"));   
  3. System.out.println(0.1f);  
        System.out.println(new BigDecimal(0.1f));          System.out.println(new BigDecimal("0.1"));          System.out.println(0.1f);  


结果是不一样的

更加更加更加深入的话

Java代码 复制代码 收藏代码Java中容易犯错的一个地方(转) - 空想主义者 - 空想主义者的博客
  1. if (0.1f + 0.1f + 0.1f != 0.3f)   
  2.     System.out.println("0.1 + 0.1 + 0.1 is not equal to 0.3");   
  3. else  
  4.     System.out.println("0.1 + 0.1 + 0.1 is equal to 0.3");   
  5.   
  6. if (0.1 + 0.1 != 0.2)   
  7.     System.out.println("0.1 + 0.1 is not equal to 0.2");   
  8. else  
  9.     System.out.println("0.1 + 0.1 is equal to 0.2");  
        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相加呢

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

Java代码 复制代码 收藏代码Java中容易犯错的一个地方(转) - 空想主义者 - 空想主义者的博客
  1. System.out.println(new BigDecimal(0.2d));   
  2. System.out.println(new BigDecimal(0.1d).add(new BigDecimal(0.1d)));   
  3.   
  4. System.out.println(new BigDecimal(0.2f));   
  5. System.out.println(new BigDecimal(0.1f).add(new BigDecimal(0.1f)));   
  6.   
  7. System.out.println(new BigDecimal(0.3d));   
  8. System.out.println(new BigDecimal(0.1d).add(new BigDecimal(0.1d)).add(new BigDecimal(0.1d)));   
  9.   
  10. System.out.println(new BigDecimal(0.3f));   
  11. System.out.println(new BigDecimal(0.1f).add(new BigDecimal(0.1f)).add(new BigDecimal(0.1f)));  
        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)));    


原创粉丝点击