【问题标题】:Floating point arithmetic and reproducibility浮点运算和再现性
【发布时间】:2014-02-08 08:14:30
【问题描述】:

IEEE-754 算法能否在不同平台上重现?

我正在测试一些用 R 编写的使用随机数的代码。我认为在所有测试平台上设置随机数生成器的种子将使测试可重现,但对于rexp() 似乎并非如此,它生成指数分布的随机数。

这是我在 32 位 Linux 上得到的:

options(digits=22) ; set.seed(9) ; rexp(1, 5)
# [1] 0.2806184054728815824298
sessionInfo()
# R version 3.0.2 (2013-09-25)
# Platform: i686-pc-linux-gnu (32-bit)

这就是我在 64 位 OSX 10.9 上得到的:

options(digits=22) ; set.seed(9) ; rexp(1, 5)
# [1] 0.2806184054728815269186
sessionInfo()
# R version 3.0.2 (2013-09-25)
# Platform: x86_64-apple-darwin10.8.0 (64-bit)

64 位 Linux 提供与 64 位 OSX 相同的结果,因此这似乎是 32 位与 64 位的问题。

让我们假设两个 R 版本都使用相同的 GCC 版本进行编译,并且使用相同的(默认 R)编译标志,使编译器使用 IEEE-754 算法。

我的问题是,这可以被认为是 R 中的错​​误吗?还是只是使用近似的有限精度浮点运算的“正常”结果?

我向 R-devel 邮件列表发送了同样的问题,但在列表中没有得到任何答案,而且只有一个私下的答案,试图让我相信这不是一个错误,我应该忍受它。

这是 IEEE-754 关于再现性的说法(来自维基百科):

IEEE 754-1985 允许多种实现方式(例如 某些值的编码和某些异常的检测)。 IEEE 754-2008 已收紧其中许多,但有一些变化 仍然存在(尤其是二进制格式)。再现性 条款建议语言标准应提供一种手段 编写可重现的程序(即,将产生相同的程序 导致一种语言的所有实现),并描述了需要什么 以达到可重复的结果。

这是在“建议”下。

我的(主观)意见是这是一个错误,因为 IEEE-754 标准的重点在于具有可重现的、独立于平台的浮点运算。

【问题讨论】:

  • digits=22 超出了您的机器限制;请参阅.Machine$double.eps,它提前大约六位数结束。
  • @DirkEddelbuettel 我不确定这是否重要,IEEE-754 的建议是“提供一种编写可重现程序的方法”。例如。如果它可以重现 16 位数字,但不能重现,那么 R 应该在那里截断。我的问题不是操作不准确,那很好。问题是相同的代码给出不同的结果。 IEEE-754 允许你犯错。但它建议你总是以同样的方式犯错。
  • 也许关于如何截断的最佳实践确实有用。我很满意set.seed(42); print(runif(3), digits=16) 在 i386、x86_64 和我的 Android 手机(后者运行 free R Console app)上给我相同的结果。
  • 是的,它实际上不是 RNG(可能),而是从统一的随机数创建一个指数分布的随机数。这并不奇怪,AFAIK RNG 通常是整数算术。但是指数是浮点运算。这就是为什么runif() 很好。

标签: r floating-point ieee-754


【解决方案1】:

在高级语言中,即使是基本的浮点运算也存在重现性问题,但它们通常可以通过各种特定于平台的操作来控制,例如设置编译器开关、使用自定义代码设置浮点控件和模式、或者,如有必要,在汇编中编写基本操作。在 cmets 中开发,您遇到的具体问题可能是不同的 C 实现使用不同的精度来计算中间浮点表达式。这通常可以通过编译器开关来控制,或者通过在表达式中包含强制转换和赋值来要求四舍五入到标称类型(从而丢弃多余的精度)。

但是,更复杂的函数,例如expcos,通常无法在不同平台上重现。尽管 2008 年 IEEE-754 标准建议使用正确的舍入来实现这些函数,但对于任何运行时具有已知界限的数学库,此任务尚未完成。 世界上没有人做过数学来实现这一点。

CRlibm project 已经实现了一些已知运行时界限的功能,但工作并不完整。 (根据 Pascal Cuoq 的评论,当 CRlibm 没有经过验证的正确舍入的运行时界限时,由于计算精度非常高,它会退回到很可能被正确舍入的结果。)弄清楚如何正确传递-rounded 结果在有限的时间内,并证明它对于许多功能来说是困难的。 (考虑如何证明cos(x) 的任何值,其中x 是任何double 值,距离两个可表示值之间的中点的距离小于e。中点很重要,因为它是舍入必须从返回一个结果更改为返回另一个结果,e 告诉您必须如何准确和精确地计算近似值才能提供正确的舍入。)

目前的情况是,数学库中的许多函数都是近似的,提供的精度比正确的舍入要松,而且不同的供应商使用不同的实现和不同的近似值。我假设R 在其rexp 实现中使用了其中一些函数,并且它使用了其目标平台的本机库,因此在不同的平台上会得到不同的结果。

要解决这个问题,您可以考虑在您的目标平台上使用通用数学库(可能是 CRlibm)。

【讨论】:

  • 感谢您的解释,我当然不认为这很容易,但我希望它是为一组基本操作完成的,其余的来自这些。我仍然认为在这种特殊情况下,这是一个可以修复的错误(我有一天会尝试),因为这就是 rexp 所做的一切:github.com/wch/r-source/blob/…unif_rand() 似乎可以跨平台重现。跨度>
  • @GaborCsardi:假设unif_rand 也不包含任何非基本函数,我怀疑区别在于 32 位 Linux 的 GCC 编译在其算术中使用了额外的精度 (long double) ,即使名义类型是double。可能有一个开关来控制这个。
  • 从技术上讲,CRlibm 提供了具有已知运行时界限的有限执行时间:对于不知道最坏情况的函数的非精确情况,它只会将计算精度提高到 512 位。这意味着对于某些函数,它不是正确四舍五入,而是“以天文置信度正确四舍五入”。替代方案在lipforge.ens-lyon.fr/frs/download.php/153/crlibm-1.0beta3.pdf 的第 1.3.5 节中讨论
  • 上一条评论中的“512 bits”部分是我无法再次找到来源的回忆。无论如何,都是“天文数字”。
  • @PascalCuoq:谢谢,我在答案中加入了关于此的注释。
猜你喜欢
  • 2012-08-10
  • 1970-01-01
  • 1970-01-01
  • 2020-06-17
  • 2016-08-23
  • 2012-12-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多