浮点不准确性有多确定性?

Gen*_*ror 25 c# math floating-point silverlight

我知道浮点计算存在准确性问题,并且有很多问题可以解释原因.我的问题是,如果我运行两次相同的计算,我是否可以始终依赖它来产生相同的结果?哪些因素可能会影响这个?

  • 计算之间的时间?
  • CPU的当前状态?
  • 不同的硬件?
  • 语言/平台/操作系统?
  • 太阳耀斑?

我有一个简单的物理模拟,并希望记录会话,以便他们可以重播.如果可以依赖计算,那么我只需要记录初始状态加上任何用户输入,我应该始终能够完全重现最终状态.如果计算不准确,那么在开始时错误可能会在模拟结束时产生巨大影响.

我目前在Silverlight工作,但有兴趣知道这个问题是否可以回答一般.

更新: 初始答案表明是,但显然这并不完全清楚,如所选答案的评论中所述.看起来我将不得不做一些测试,看看会发生什么.

Jas*_*ins 20

根据我的理解,只有您处理相同的指令集和编译器,并且您运行的任何处理器严格遵守相关标准(即IEEE754),您才能保证相同的结果.也就是说,除非你正在处理一个特别混乱的系统,否则运行之间的任何计算偏差都不可能导致错误的行为.

我所知道的特定陷阱:

1.)某些操作系统允许您以破坏兼容性的方式设置浮点处理器的模式.

2.)浮点中间结果通常在寄存器中使用80位精度,但在内存中只有64位.如果以更改寄存器溢出函数的方式重新编译程序,则与其他版本相比,它可能返回不同的结果.大多数平台都会为您提供一种方法来强制将所有结果截断为内存精度.

3.)标准库函数可能在版本之间发生变化.我认为在gcc 3 vs 4中有一些不常见的例子.

4.)IEEE本身允许一些二进制表示不同......特别是NaN值,但我不记得细节.

  • @Jason Watkins:NaN只有两个逻辑表示,静默和信号,但NaN有许多二进制表示.否则+1好东西. (2认同)
  • #1 在 Windows 上尤其重要。DirectX 的某些版本会将 CPU 置于较低精度模式,从而导致意外结果。 (2认同)
  • 关于 CPU 实现,这取决于您使用的语言。C 对于您将获得的 FP 相当不具体。C# 和 Java 指定了 IEEE754 语义,然后实现的工作就是隐藏处理器的实际功能。如果我在旧的 VAX 或损坏的 Pentium 上运行 Java,那么我会期望看到 IEEE754 行为,尽管这不是处理器实现的,因为语言定义要求这样做。如果我不这样做,JVM 就会按照定义被破坏。 (2认同)
  • @ijw:实际上,JVM 故意破坏了双精度/浮点数。您需要“strictfp”关键字来使其严格符合IEEE754,但这可能会使程序变慢。 (2认同)
  • 虽然许多人认为 80 位类型是 x87 的怪癖,但它实际上被设计为比 64 位双精度*在没有浮点单元的机器上*运行得更快。“经典”Macintosh 从未使用过 8x87,但它进行浮点计算的方式与 8x87 非常相似。 (2认同)

Tod*_*lin 17

简而言之,根据IEEE浮点标准,FP计算完全是确定性的,但这并不意味着它们可以在机器,编译器,操作系统等之间完全重现.

这些问题的长期答案可以在浮点数的最佳参考中找到,David Goldberg的"每个计算机科学家应该知道的关于浮点算术的内容".有关详细信息,请跳至IEEE标准部分.

简要回答一下你的要点:

  • 计算和CPU状态之间的时间与此无关.

  • 硬件会影响事物(例如,某些GPU不符合IEEE浮点).

  • 语言,平台和操作系统也会影响事物.为了更好地描述这一点,请参阅Jason Watkins的回答.如果您使用Java,请查看Kahan 对Java浮点不足咆哮.

  • 太阳耀斑可能很重要,希望不经常发生.我不会太担心,因为如果它们确实重要,那么其他一切也都搞砸了.我会把它放在与担心EMP相同的类别中.

最后,如果您在相同的初始输入上执行相同的浮点计算序列,那么事情应该可以完全重放.确切的顺序可能会根据您的编译器/ os /标准库而改变,因此您可能会以这种方式获得一些小错误.

如果你有一个数值不稳定的方法并且你开始使用大致相同但不完全相同的FP输入,那么你通常遇到浮点问题.如果您的方法稳定,您应该能够在一定的容差范围内保证重现性.如果您想了解更多细节,请查看上面链接的Goldberg的FP文章,或者获取有关数值分析的介绍文本.

  • 请参阅我对 @JaredPar 的回复,有很多因素可能会导致两个符合 IEEE 规范的实现之间的计算出现差异。说计算是确定性的并不是特别有帮助,因为确定性并不一定意味着可重现。 (2认同)

Jar*_*Par 8

我认为你的困惑在于浮点周围的不准确类型.大多数语言都实现了IEEE浮点标准.该标准规定了如何使用float/double中的各个位来生成数字.通常,浮点数由四个字节和一个双八字节组成.

两个浮点数之间的数学运算每次都具有相同的值(在标准中指定).

精度不准确.考虑一个int与一个浮点数.两者通常占用相同的字节数(4).然而,每个数字可以存储的最大值是完全不同的.

  • int:大约20亿
  • float:3.40282347E38(相当大)

区别在于中间.int,可以表示0到大约20亿之间的每个数字.然而,Float不能.它可以代表0到3.40282347E38之间的20亿个值.但这留下了一系列无法​​表达的价值观.如果数学等式达到这些值中的一个,则必须将其四舍五入为可表示的值,因此被认为是"不准确的".您对不准确的定义可能会有所不同:).

  • 这掩盖了不那么明确的可再现性方面。IEEE 做出了某些保证,但这些保证基于严格的假设,并且不会扩展到所有操作或库函数。@jason-watkins 很好地解释了他的回答中的主要问题。 (2认同)