IEEE(二进制)舍入规则是否应用于精确的十进制输入?

时间:2015-10-21 09:23:14

标签: floating-point rounding ieee-754 bankers-rounding

假设我使用默认的round-to-half-even规则将数字1.20515转换为IEEE兼容语言(C,Java等)中的4位小数,结果将为“1.2051”这甚至不是。

我认为这是因为1.20515在存储为二进制文件时略微偏向1.2051,因此二进制空间中甚至没有关系。

但是,如果输入1.20515精确到小数,那么这种舍入实际上是不是错了?

编辑:

我真正想知道的是,如果我不想使用精确的十进制算术(例如Java的BigDecimal),这些二进制舍入规则是否会在工作流中引入偏差:字符串中的精确十进制(最大6 dp) ) - >解析到IEEE双 - >使用IEEE规则将其转换为4 d.p。

编辑2:

“精确十进制”输入由Java使用直接来自数据库的BigDecimalString生成。不幸的是,格式化必须在JavaScript中完成,它缺乏对正确舍入的大量支持(我正在研究实现一些)。

1 个答案:

答案 0 :(得分:2)

您是正确的:1.20515不能代表IEEE754 binary64,因此小数 - >二进制转换将舍入到最接近的值,即1.2051499999999999435118525070720352232456207275390625。

IEEE754标准实际上没有任何关于将二进制值舍入为非整数小数的说法(舍入到最接近的整数并不会遇到此问题),因此任何此类功能都取决于语言标准(如果它选择定义它)。 JavaScript toFixed明确将其定义为确切的数学值(即1.2051)。

(更新:实际上,IEEE754标准确实指定了如何执行FP - >字符串转换,请参阅Stephen Canon在下面的评论。)

但是如果你想要对整个管道进行正确的舍入,你可以改为

function roundeven(x) {
    return Math.sign(x)*((Math.abs(x) + 4.503599627370496e15) - 4.503599627370496e15);
}

roundeven(Math.round(parseFloat(s)*1e6)/1e2)/1e4;

只要s少于16位(即绝对值小于10 9 ),它就会起作用。

为什么会这样?

  • Math.round(parseFloat(s)*1e6)是准确的:这是因为binary64可以正确地往返最多15个十进制数字,这通过缩放到整数值基本上是相同的。
  • 1e2将涉及一些舍入(因为并非所有值都可以准确表示),但重要的是,它可以(i)精确地表示具有小数半部分的值,并且(ii)不会围绕任何值其他值为小数部分(因为我们仍然少于16位小数)。
  • roundeven实现与最接近的整数的绑定到均匀舍入。此实现适用于上述范围内的任何值。
  • 最终除法将再次涉及一些舍入,但值将最接近正确的十进制值,因此转换回字符串(如果需要,请通过_.toFixed(2))将得到正确的结果。< / LI>

(感谢bill.cn和Mark Dickinson的更正)