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



假设我在ieee兼容的语言(C, Java等)中使用默认的四舍五入到半偶数规则将数字1.20515舍入到小数点后4位,结果将是"1.2051",这不是偶数。

我认为这是由于1.20515在二进制存储时稍微偏向1.2051,所以在二进制空间中甚至没有平局。

但是,如果输入的1.20515小数是精确的,那么这种四舍五入实际上不是错误的吗?

编辑:

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

编辑2:

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

你是正确的:1.20515不能用IEEE754 binary64表示,所以十进制->二进制转换将四舍五入到最接近的值1.20514999999999435118525070720352232456207275390625。

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位(即绝对值小于109),

就可以工作。

为什么会这样?

  • Math.round(parseFloat(s)*1e6)是精确的:这是因为binary64可以正确地往返15个十进制数字,这基本上是通过缩放到整数值来做同样的事情。
  • 1e2将涉及一些四舍五入(因为不是所有的值都可以精确表示),但重要的是,它可以(i)精确地表示小数一半的值,并且(ii)不会将任何其他值四舍五入为小数一半(因为我们仍然有少于16个十进制数字)。
  • roundeven实现四舍五入到最接近的整数。此实现对上述范围内的任何值都有效。
  • 最后的除法将再次涉及一些四舍五入,但值将最接近正确的十进制值,因此转换回字符串(当需要时,通过_.toFixed(2))将给出正确的结果。

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

最新更新