提问人:Nova 提问时间:7/18/2023 最后编辑:CommunityNova 更新时间:7/20/2023 访问量:134
Java 浮点格式化和舍入
Java float formatting and rounding
问:
我一直在尝试 Java 浮点数,我注意到一些奇怪的行为,这些行为似乎不会发生在其他语言中,比如 C#。
// Expecting 'x' to round down to 1500.455.
float x = 1500.45504f;
// Expecting 'y' to round up to 2000.3741.
float y = 2000.37408f;
System.out.println(x + ", " + y);
上面的代码生成输出。两个浮点数的舍入是否应该不一致?为什么 1101.45504 四舍五入为 ,而 1106.37408 的值基本上被截断为 ?难道不应该反过来吗?1101.4551, 1106.374
.4551
.374
编辑:这个问题背后的主要意图是询问为什么不同的语言以不同的方式对待浮点数。正如 Eric 所提到的,Java 的方法确实多附加了一个数字来区分相邻的浮点数,但为什么 C# 不是这样呢?以及如何用其他语言(例如 C#)表示这种行为。Float.ToString()
答:
两个浮点数的舍入是否应该不一致?
这两个值的舍入是一致的。它不会四舍五入到十进制数字。它四舍五入为二进制数字,有 24 位有效位。float
在二进制中,1500.45504 是 10111011100.011101000111101100000000101111001110101...。将其四舍五入为 24 位将生成 10111011100.011101001,即 1500.455078125。
在二进制中,2000.37408 是 11111010000.01011111110000111011011011011000010110...。将其四舍五入为 24 位将生成 11111010000.0101111111000,即 2000.3740234375。
用于将其转换为十进制数字的默认格式会生成足够的十进制数字来唯一区分数字。对于 1500.455078125,其下方和上方的相邻值为 1500.4549560546875 和 1500.4552001953125。如果其中任何一位四舍五入到七位数字(小数点后三位),则所有结果都将为“1500.455”。因此,还需要一位数字来区分 1500.455078125 与其他数字。因此,它被四舍五入到八位十进制数字,产生“1500.4551”。float
float
同样,对于 2000.3740234375,相邻的值为 2000.3739013671875 和 2000.3741455078125,将其中任何一位四舍五入为七位数字将生成“2000.374”。但是,在这种情况下,将它下面和上面的数字四舍五入到八位数字可以区分它们,产生“200.3739”和“2000.3741”,因此算法允许我们的数字 2000.3740234375 使用七位数字,产生“2000.374”,因为它知道这唯一区分它,因为其他数字会产生不同的结果。float
评论
Java 和 C# 中的浮点数使用 IEEE 754 单精度格式,其精度为 7 个十进制数字(嗯,略高于 7)。这意味着,当您定义一个十进制数超过 7 位(如 1500.45504f)的浮点数变量时,该值将四舍五入到最接近的可表示浮点值。在这两种语言中,变量将保存相同的二进制值(可以很容易地验证)。
您看到的差异与这些语言在其标准库中默认实现浮点舍入和精度的方式有关。特别是,区别在于它们如何四舍五入最后一位数字以及打印的位数。您可以在 Java 实现中阅读它:Float.toString(float) 和 C#:System.Single.ToString。
您可以通过摆弄格式说明符来使输出看起来完全相同:
在 C# 中:
float x = 1500.45504f;
float y = 2000.37408f;
Console.WriteLine("{0:G9}, {1:G9}", x, y);
输出:
1500.45508, 2000.37402
在 Java 中:
float x = 1500.45504f;
float y = 2000.37408f;
System.out.printf("%.9g, %.9g%n", x, y);
输出相同:
1500.45508, 2000.37402
有趣的是,在 Java 的默认行为的情况下,您可以将浮点数转换为字符串并转换回浮点数,并具有完全相同的值。如果是 c#,则两者都不是真的 - 要补充它 - 在调用时需要一个往返标志: .float_variable.ToString("R")
评论
new BigDecimal(x)
1500.455078125
new BigDecimal(y)
2000.3740234375
1101.4551, 1106.374
1500.4551, 2000.374
)