为什么浮动价值" ++" 3.14的操作不同?

时间:2018-05-18 09:46:54

标签: java

代码

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 ......

为什么这种情况特殊?

2 个答案:

答案 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;浮动值没有很好地定义值。

请参阅:Is floating point math broken?