假设我使用默认的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使用直接来自数据库的BigDecimal
或String
生成。不幸的是,格式化必须在JavaScript中完成,它缺乏对正确舍入的大量支持(我正在研究实现一些)。
答案 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的更正)