Java在将整数存储为浮点数时如何进行四舍五入

3
这是一个经典问题:你的遗留代码使用浮点数,但实际上应该使用整数。但是,更改代码中每个变量(或多个变量)太过昂贵。因此,您需要编写自己的舍入函数,它需要一堆参数来提高精度并转换为整数。
所以,基本问题是,在Java中制作浮点数时如何进行四舍五入?经典示例是0.1,通常被引用为四舍五入到0.0999999999998(或类似的值)。但是,当给定整数时,浮点数是否总是向下舍入到它可以表示的下一个值?它是否将其内部尾数向下舍入以有效地舍入其绝对值?还是只选择整数和新浮点数之间误差最小的值?
当调用“Float.parseFloat(String)”时,如果字符串是整数,例如“1234567890”,行为是否不同?当字符串是比Float可以存储的精度更高的浮点数时,行为是否也相同?
请注意,我在使用浮点数或引用Float时,会交替使用Double。整数和长整数也是如此。

1
在将整数转换为浮点数时,没有必要进行“四舍五入”。最坏的情况是整数中的有效位数超过浮点形式中的小数位数,但这只对相当大的数字造成问题。 - Hot Licks
可能是Java中的浮点数转换的重复问题。 - Hot Licks
1个回答

5
如何在Java中处理浮点数的舍入问题?
在Java中,当您使用构造函数 (int) d 时,会向零舍入(即截断),其中 d 的类型为 doublefloat。如果您需要四舍五入到最接近的整数,则可以使用以下语句:
int a = (int) Math.round(d);

经典例子是0.1,经常被引用为四舍五入为0.0999999999998(或类似的数字)。对于整数,不存在您所提到的问题,因为它们可以精确地表示为double(对于介于-2^53和2^53之间的整数)。如果您要舍入的数字来自先前应该产生整数但由于浮点舍入误差可能没有产生整数的计算,则(int)Math.round(d)可能是您应该使用的解决方案。这意味着只要累积误差不超过0.5,您将获得正确的整数。
如果生成双精度d的计算仅涉及+,-,*与其他整数的计算,产生-2^53和2^53之间的中间结果,则d自动包含一个整数(它是精确的,因为涉及的浮点计算是精确的,它是一个整数,因为精确结果是一个整数),您可以使用更简单的(int)d进行转换。另一方面,如果涉及除法或非整数操作数,则不应轻易更改d的类型,因为它会改变这些计算的结果。
当字符串是像"1234567890"这样的整数时,调用Float.parseFloat(String)的行为是否不同?这将产生一个float值,其值是最接近有理数1234567890的可表示单精度值。这恰好是1234567936.0f。
当字符串是比Float可以存储的更高精度的浮点数时,行为是否也相同?从技术上讲,“0.1”比Float可以存储的更高精度。同样,从技术上讲,之前的例子1234567890也比Float可以存储的更高精度。行为是相同的:Float.parseFloat("0.1")会产生最接近有理数0.1的float值。

所以,为了明确起见,在Java中从大整数转换为浮点数(小写)时,它将使用浮点数可以表示的最近数字。对于长整型到双精度浮点型也是如此。此外,您是否有参考资料,因为它可能会提供更多对我和其他遇到类似问题或疑问的人有帮助的信息。 - Erik Helleren
是的。是的。Java遵循IEEE 754标准的原则,该标准规定基本转换(十进制<->二进制)应根据当前舍入模式进行。对于从浮点数到整数的转换(通过截断),Java遵循C传统,这是早于IEEE 754标准的。最后,不要担心其他人有类似的问题或疑问,他们不会比您阅读现有文档(包括Java语言定义、浮点数的维基百科页面等)更多地阅读此答案。 - Pascal Cuoq
2
Java语言规范[5.1.2.扩展原始转换] (http://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.2)指出:“从int到float,或从long到float,或从long到double的扩展原始转换可能会导致精度损失 - 也就是说,结果可能会丢失一些最不重要的值。在这种情况下,使用IEEE 754舍入至最近模式(§4.2.4),得到的浮点值将是整数值的正确舍入版本。” - Patricia Shanahan
1
请给我点评,我很乐意听听您对这个答案的不满意之处。 - Pascal Cuoq

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接