不同编程语言中奇怪的“银行家舍入法”

12

GNU bash 4.2.24版本:

$> printf "%.0f, %.0f\n" 48.5 49.5
48, 50

Ruby 1.8.7

> printf( "%.0f, %.0f\n", 48.5, 49.5 )
48, 50

Perl 5.12.4

$> perl -e 'printf( "%.0f, %.0f\n", 48.5, 49.5 )'
48, 50

gcc 4.5.3:

> printf( "%.0f, %.0f\n", 48.5, 49.5 );
48, 50

GHC,版本7.0.4:

> printf "%.0f, %.0f\n" 48.5 49.5
49, 50
维基百科称这种舍入方式为“四舍六入五取偶”:
默认情况下,IEEE 754 计算函数和运算符使用此舍入模式。
为什么 C、Perl、Ruby 和 bash 默认使用此舍入方式,而 Haskell 不使用? 这是一种传统或标准吗? 如果是标准,为什么这些语言使用此方法,而 Haskell 则不使用? 四舍六入五取偶的意义是什么?

4
我想你已经回答了自己的问题。这是IEEE 754标准的一部分。 - Keith Irwin
@Keith Irwin,我相信他想知道哪个标准。为什么不把它作为答案发布出来,最好附上相关链接? - ikegami
2
Dmitry:你的问题特别提到了“IEEE 754”。那是标准。 - Gabe
1
如果你知道一些西里尔字母,转写并不难。 - augustss
2
Haskell中的printf应该改为使用银行家舍入法。 - augustss
显示剩余8条评论
3个回答

11
GHCi> round 48.5
48
GHCi> round 49.5
50
唯一的区别是printf没有使用round,这可能是因为它必须能够四舍五入到不仅仅是整数。我认为IEEE 754并没有规定如何实现printf格式化函数,只有关于舍入的规定,而Haskell正确地执行了此操作。
最好printfround和其他语言的实现保持一致,但我认为这并不是一个大问题。

printf风格的格式化函数将转换为定点小数格式。IEEE 754建议在当前舍入模式下进行转换,默认情况下是最近偶数。请注意,截至2012年,许多语言/平台仍然会在转换时错误地使用最近偶数,而不考虑当前的舍入模式。 - Pascal Cuoq
5
@PascalCuoq:谢谢提供信息!然而,Haskell 简单地无法遵循那个建议,因为 printf 必须是一个纯函数。它必须将舍入模式作为参数传递,或在上下文中操作(例如,单子上下文),以使该信息可用。 - ehird
好的观点。像IEEE 754这样的语言无关标准存在很多歧义,可能不方便适应。另一个相对类似的情况是Hoare逻辑验证命令式浮点程序,在这种情况下,您最好避免将舍入模式视为程序状态的附加部分和所有浮点函数的参数,但您必须这样做才能处理在运行时修改它的程序。 - Pascal Cuoq

6
"Round to even" 是 IEEE 754 的默认设置。为了保持一致性,Haskell 在 printf 中应该考虑使用它。相关的代码行在 GHC.Float 中。
f 0 (x:_)  = (if x >= b2 then 1 else 0, [])

因此,如果有人想修复它,他们可以这样做。正如ehird所指出的那样,这只是使printf使用的roundTo函数与round一致,尽管我不确定这种更改会破坏哪些其他代码。

编辑:本答案的先前版本错误地定位了舍入代码的位置。两个实现之间唯一重要的区别是它们是否硬编码为使用基数10。


2
我认为那不是正确的代码行。Text.Printf 适用于 String,而不是 Text - ehird
@ehird Text.Printf 调用了一个名为 dfmt 的辅助函数,该函数又调用了另一个名为 dfmt 的辅助函数,后者从 Numeric 中调用了 showFFloat。从那里开始它的实现取决于具体情况,当我回答这个问题时,我认为我已经追踪到 GHC 代码到 Data.Text 模块,现在我需要再次确认。 - Philip JF
看起来你是正确的:从 GHC 7.4.1 开始,formatRealFloat 调用的 roundTo 函数包含在该文件中,而不是调用 Data.Text.etc。它做的事情相同,但是具有基数感知能力。 - Philip JF

2

我不能百分之百确定,但这可能与这种类型的四舍五入通常在会计函数中使用有关,因为这也被称为银行家舍入。如果您进一步查看维基百科关于四舍五入的文章,您还会注意到这是IEEE 754的默认值,所以很可能Haskell没有遵循该标准。


1
链接的维基文章指出:“银行家舍入术的起源仍然更加模糊。如果这种舍入方法曾经是银行业的标准,那么证据就极其难以找到。相反,欧洲委员会报告《欧元的引入和货币金额的舍入》第2节表明,在银行业中以前没有标准的舍入方法;并且它指定“半数”金额应该被__向上舍入__。”因此,银行家们__不使用__银行家舍入法。 - A.H.
@A.H. 实际上,他们仍然在有限的范围内使用银行家舍入法。我可以确信地说,因为我曾经在一个银行集团工作过。 - Tom A
我也有金融机构中使用的四舍五入的第一手知识,但不是在美国。但这里的受众也是国际化的。 - A.H.
3
@A.H. - 显然,他们大约一半的时间使用银行家舍入法。 - mob
1
@A.H. 总是向上取整会引入偏差。这是一个大忌讳。这就是为什么没有任何默认产生数值不准确的结果。通过向偶数舍入,您可以分摊差异并消除偏差。任何建议总是向上取整的人都是白痴或骗子。 - tchrist

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