为什么 8099.99975f != 8100f?

Ich*_*aum 1 java floating-point ieee-754 single-precision

编辑:我知道浮点运算并不精确。算术甚至不是我的问题。添加给出了我预期的结果。8099.99975f没有。


所以我有这个小程序:

public class Test {
    public static void main(String[] args) {
        System.out.println(8099.99975f); // 8099.9995
        System.out.println(8099.9995f + 0.00025f); // 8100.0
        System.out.println(8100f == 8099.99975f); // false
        System.out.println(8099.9995f + 0.00025f == 8099.99975f); // false
        // I know comparing floats with == can be troublesome
        // but here they really should be equal in every bit.
    }
}
Run Code Online (Sandbox Code Playgroud)

我编写它是为了检查当编写为 IEEE 754 单精度浮点数时是否8099.99975四舍五入。8100令我惊讶的是,Java 将其转换为8099.9995浮点文字 ( 8099.99975f)。我再次检查了我的计算和 IEEE 标准,但没有发现任何错误。与 一样8100远,但最后一位应该使其成为正确的表示。8099.999758099.999581000

所以我检查了 Java 语言规范,看看我是否遗漏了一些东西。经过快速搜索,我发现了两件事:

  • Java 编程语言要求浮点算术的行为就像每个浮点运算符将其浮点结果四舍五入到结果精度一样。不精确的结果必须四舍五入到最接近无限精确结果的可表示值;如果两个最接近的可表示值同样接近,则选择最低有效位为零的值。

  • Java 编程语言在将浮点值转换为整数时使用向零舍入的方法[...]。

我注意到这里没有提到浮点文字。所以我认为浮点文字可能只是双精度数,当转换为浮点时,其四舍五入为零,类似于浮点到整数转换。这可以解释为什么8099.99975f四舍五入为零。

我编写了一个小程序,您可以在上面看到它来检查我的理论,并且确实发现,当添加两个浮点文字时,应该会计算出8100正确的浮点值。(这里请注意,8099.9995and0.00025可以完全表示为单个浮点数,因此没有可能导致不同结果的舍入)这让我很困惑,因为浮点数文字和计算浮点数的行为不同对我来说没有多大意义,所以我深入研究了进一步查看语言规范并发现:

如果浮点文字以 ASCII 字母 F 或 f [...] 为后缀,则其类型为 float。float [...] 类型的元素是可以使用 IEEE 754 32 位单精度[...] 二进制浮点格式表示的值。

这最终表明文字应根据 IEEE 标准进行舍入,在本例中为8100。那么这是为什么呢8099.9995

Daw*_*ica 5

要认识到的关键点是浮点数的值可以通过两种不同的方式计算出来,这两种方式通常并不相等。

  • 浮点数中的位给出了精确的二进制表示形式的值。
  • 浮点数有“小数显示值”,它是比任何其他数字更接近该浮点数的小数位数最少的数字。

要了解差异,请考虑指数为 10001011、有效数为 1.11111010001111111111111 的数字。这是 8099.99951171875 的精确二进制表示。但十进制值 8099.9995 的小数位较少,并且比任何其他浮点数更接近该浮点数。因此,8099.9995 是打印该数字时将显示的值。

请注意,这个特定的浮点数是 8100 之后的下一个最低浮点数。

现在考虑 8099.99975。它比 8100 更接近 8099.99951171875。因此,为了用单精度浮点数表示它,Java 将选择浮点数,即 8099.99951171875 的精确二进制表示形式。如果您尝试打印它,您将看到 8099.9995。

最后,当您在单精度浮点中执行 8099.9995 + 0.00025 时,所涉及的数字是 8099.99951171875 和 0.0002499999827705323696136474609375 的精确二进制表示形式。但由于后者略大于 1/2^12,加法的结果将更接近 8100,而不是 8099.99951171875,因此最后会向上舍入,而不是向下舍入,使其成为 8100。

  • 请注意,0.9995 = 1999/2000。但由于 2000 不是 2 的幂,因此该分数不可能有终止二进制表示。换句话说,任何精度级别都不可能有浮点数,即 0.9995。添加 8099 没有什么区别。无论您使用什么精度,近似 8099.9995 的浮点数永远不可能完全是该数字。 (2认同)