java - 为什么浮点值 "++"操作对于 3.14 不同?

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

为什么这个案例很特别?

最佳答案

有很多数字可以精确地用十进制表示,但不能精确地用二进制表示。也就是说,它们具有终止十进制表示,但没有终止二进制表示。

要理解这一点,请考虑数字 1/3。它没有终止十进制表示法——我们可以继续写 0.3333333333333 一段时间,但迟早我们必须停下来,而且我们还没有写完 1/3。

当我们尝试用二进制写 2.14 时,同样的事情也会发生。它是 10.001000111... 以及最终开始重复的更多 0 和 1,就像 0.333333 以十进制重复一样。

现在 double 只是一个有 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 有一个复杂的算法来确定如何显示 float 。基本上,它会选择最短的十进制表示形式,它更接近您要表示的 float ,而不是任何其他 float 。这样,如果你写 double d = 2.14; , 然后你会得到一个非常接近 2.14 的 float ,当你打印出来时它总是显示为 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 的幂。

关于java - 为什么浮点值 "++"操作对于 3.14 不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50408476/

相关文章:

java - Apache Camel : async operation and backpressure

c# - 您希望设计架构具备哪些品质,以便编码团队可以轻松实现?

java - 如何解决 jms 中无效的连接参数?

java - 使用迭代值增量的趋势分析

java - Android Studio 构建失败

java - MouseEvent.getX() 的 Java.awt.component 的起源

java - 这个单例类会导致多线程问题吗

java - Spring Controller 的工作方式与 GET 和 POST 方法类似

java - 将 RestAPI Slim 框架连接到 Android 应用程序

java - 将配置从 XML 更改为注释