【问题标题】:Strange "half to even" rounding in different languages [closed]不同语言的奇怪“一半到偶数”舍入[关闭]
【发布时间】:2012-04-07 10:57:10
【问题描述】:

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

维基百科说这种四舍五入叫做round half to even

这是 IEEE 754 计算函数和运算符中使用的默认舍入模式。

为什么在 C、Perl、Ruby 和 bash 中默认使用这种舍入,而在 Haskell 中却没有?

这是某种传统还是标准?如果它是一个标准,为什么它被那些语言使用而不被 Haskell 使用?四舍五入有什么意义?

【问题讨论】:

  • 我想你刚刚回答了你自己的问题。它是 IEEE 754 标准的一部分。
  • @Keith Irwin,我相信他想知道哪个标准。您为什么不将其发布为答案,希望有相关链接?
  • Dmitry:您的问题特别提到了“IEEE 754”。这是标准。
  • 如果您知道几个西里尔字母,音译并不难。
  • Haskell printf 应该改为使用四舍五入。

标签: c ruby perl bash haskell


【解决方案1】:

我不能肯定地说,但这可能与这种类型的四舍五入通常用于会计函数的事实有关,因为这也被称为银行家四舍五入。如果您进一步查看有关舍入的 Wikipedia 文章,您还会注意到这是 IEEE 754 中的默认设置,因此 Haskell 可能没有遵循该标准。

【讨论】:

  • 链接的 Wiki 文章指出,“银行家四舍五入一词的起源仍然更加模糊。如果这种四舍五入方法曾经是银行业的标准,那么证据证明是极难找到的。对于相反,欧盟委员会报告 The Introduction of Euro and the Rounding of Currency Amount [16] 的第 2 节表明,以前没有标准的方法来四舍五入银行业;它规定“中途”金额应该是 四舍五入。”所以银行家不要使用银行家的四舍五入。
  • @A.H.实际上,他们仍然在一定程度上使用银行家四舍五入法。我可以用知识来说明这一点,因为我曾经在一家银行集团工作。
  • 我对金融机构使用的四舍五入也有第一手的了解。但不是在美国。但这里的观众也是国际化的。
  • @A.H. - 显然,他们使用银行家四舍五入的时间大约有一半;-)
  • @A.H.总是四舍五入会引入偏见。这是一个很大的禁忌。这就是为什么没有默认值会产生数字不准确的结果。通过四舍五入,您可以消除差异并消除偏见。任何说总是围捕的人要么是白痴,要么是骗子。
【解决方案2】:
GHCi> round 48.5
48
GHCi> round 49.5
50

唯一的区别是printf 没有使用round——大概是因为它必须能够舍入到不仅仅是整数。我认为 IEEE 754 没有具体说明如何实现 printf 样式的格式化函数,只是舍入,Haskell 正确地做到了。

如果printfround 和其他语言的实现保持一致可能是最好的,但我认为这没什么大不了的。

【讨论】:

  • printf 样式的格式化函数转换为定点十进制格式。 IEEE 754 建议在当前舍入模式下进行转换,默认情况下是最接近偶数。请注意,截至 2012 年,许多语言/平台仍然存在此错误,无论当前的舍入模式如何,都使用最接近偶数进行转换。
  • @PascalCuoq:感谢您提供的信息!但是,Haskell 根本无法遵守该建议,因为 printf 必须是一个纯函数。它必须将舍入模式作为参数,或者在该信息可用的上下文(例如 monadic)中操作。
  • 好点。诸如 IEEE 754 之类的与语言无关的标准会留下很多歧义,并且可能不方便适应。另一个相对类似的情况是命令式浮点程序的霍尔逻辑验证,您宁愿避免将舍入模式视为程序状态的附加部分,以及所有浮点函数的参数,但您必须这样做处理在运行时修改它的程序。
【解决方案3】:

“舍入到偶数”是 IEEE 754 的默认设置。出于一致性原因,Haskell 可能应该在printf 中使用它。相关的代码行在 GHC.Float

f 0 (x:_)  = (if x >= b2 then 1 else 0, [])

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

编辑:此答案的先前版本使舍入代码的位置错误。两种实现之间唯一的显着区别是它们是否被硬编码为使用 base 10。

【讨论】:

  • 我不认为这是正确的代码行。 Text.Printf 适用于 String,而不是 Text
  • @ehird Text.Printf 调用一个名为 dfmt' 的辅助函数,该函数调用另一个名为 dfmt 的辅助函数,它从 Numeric 调用 showFFloat。它的作用取决于实现,当我回答这个问题时,我以为我已经将 GHC 代码追踪到 Data.Text 模块,现在我需要仔细检查。
  • 看起来你是对的:从 GHC 7.4.1 开始,formatRealFloat 调用的 roundTo 函数包含在该文件中,而不是对 Data.Text.etc 的调用。它做同样的事情,但具有基础意识
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-30
  • 2013-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多