Java中容易犯错的一个地方(转)
来源:互联网 发布:php会员管理系统模板 编辑:程序博客网 时间:2024/06/08 01:10
直接上代码
- 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);
- }
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.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");
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();
- }
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);
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");
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)));
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)));
- Java中容易犯错的一个地方(转)
- Java中几个容易犯错的地方
- java容易犯错的地方
- java编程中容易犯错的地方之BigInteger
- python yield send 一个容易犯错的地方
- sql语句中一些容易犯错的地方
- java中容易犯错的知识点
- c语言中函数返回局部变量的指针(一些容易犯错的地方)
- 编写C程序容易犯错的地方
- 宏定义的使用容易犯错的地方---提醒
- 关于SqlServer 视图容易犯错的两个地方
- 关于equal()使用时容易犯错的地方
- [C/C++]_[初级]_[编程容易犯错的地方]
- VMware Tools 安装,初学者容易犯错的地方
- python处理字符串数据容易犯错的几个地方
- js 中容易犯错的变量声明
- iOS中容易犯错的知识点
- Java线程同步容易犯错的坑
- linux常用命令
- 第二届国信蓝点模拟试题
- linux下拷贝整个目录
- 深入理解struts2的值栈(转)
- 黑马程序员_用反射方式执行某个类中的main方法
- Java中容易犯错的一个地方(转)
- HDU 1728(BFS)
- ireport使用教程(转)
- Words In Economics
- 用C++,调用浏览器打开一个网页
- boolean android.app.Activity.isFinishing()函数使用
- Some words that will shame on you if you misunderstood of them
- the use of nvl() in oracle
- Oracle 排序中常用的NULL值处理方法(转)