代码
public class test{
public static void main(String[] args){
double first = 3.14 ;
first++;
System.out.println(first);
}
}
结果
ubuntu@john:~/Desktop$ javac test.java
ubuntu@john:~/Desktop$ java test
输出:4.140000000000001
对于几乎所有其他案例,我都得到了预期的答案...... 例如:对于4.14,结果是5.14 ......
为什么这种情况特殊?
答案 0 :(得分:2)
有很多数字可以用十进制表示,但不完全是二进制数。也就是说,它们具有终止十进制表示,但没有终止二进制表示。
要理解这一点,请考虑数字1/3。它没有终止十进制表示 - 我们可以继续写一段时间0.3333333333333,但迟早,我们必须停止,我们仍然没有写完1/3。
当我们尝试用二进制编写2.14时,会发生同样的事情。它是10.001000111 ......还有一堆更多的0和1最终开始重复,就像0.333333重复十进制一样。
现在双倍只是一个二进制数,有53位有效数字。所以它不能精确地存储2.14,但它可以非常接近。现在看看当我们开始递增时会发生什么。
2.14 = 10.001000111 .... (53 significant figures, 51 of them after the dot)
3.14 = 11.001000111 .... (53 significant figures, 51 of them after the dot)
4.14 = 100.001000111 ... (53 significant figures, 50 of them after the dot)
5.14 = 101.001000111 ... (53 significant figures, 50 of them after the dot)
因此,当我们从2.14变为3.14时,我们并没有失去任何准确性,因为点后面的部分没有改变。同样,当我们从4.14到5.14时。
但是当我们从3.14到4.14时,我们失去了准确性,因为我们在点之前需要一个额外的数字,所以我们在点之后丢失了一个数字。
现在Java有一个复杂的算法来确定如何显示浮点数。基本上,它选择的最短十进制表示更接近您尝试表示的浮点数,而不是任何其他浮点数。这样,如果你写double d = 2.14;
,那么你将得到一个浮点数,它已经关闭到2.14,当它打印出来时它总会显示为2.14。
但是一旦你开始弄乱点后面的数字,Java打印算法的复杂性就会出现 - 并且数字的打印最终可能与你期望的不同。
因此,当您增加double
时,这不会发生,但不会更改点前的位数。只有当你增加double
超过2的幂时才会发生这种情况;因为这会改变点之前的位数。
为了说明这一点,我运行了这段代码。
for(int i = 0; i < 1000000000; i++) {
if ( i + 1 + 0.14 != i + 0.14 + 1 ) {
System.out.println(i + 0.14 + 1);
}
}
并得到了这个输出。
4.140000000000001
1024.1399999999999
2048.1400000000003
4096.139999999999
1048576.1400000001
2097152.1399999997
4194304.140000001
观察到所有这些差异值都超过了2的幂。
答案 1 :(得分:1)
浮点数增量不会以1递增,而是会略微偏离。 所以它会好一段时间,但一段时间后答案将是不正确的。
这是因为四舍五入的问题&#39;浮点递增/递减。 这是一种糟糕的风格。 ++和 - 旨在将值设置为其下一个或上一个值,例如下一个或上一个整数,数组中的下一个或上一个元素(用于指针),例如
&#39;接着&#39;和之前的&#39;浮动值没有很好地定义值。