浮点运算是否稳定? [重复]

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浮点运算是否稳定? [重复]相关的知识,希望对你有一定的参考价值。

这个问题在这里已有答案:

我知道浮点数具有精度,精度后的数字不可靠。

但是如果用于计算数字的等式是相同的呢?我可以假设结果也一样吗?

例如,我们有两个浮点数xy。我们可以假设机器1的结果x/y与机器2的结果完全相同吗? I.E. ==比较将返回true

答案

但是如果用于计算数字的等式是相同的呢?我可以假设结果也一样吗?

不,不一定。

特别是,在某些情况下,允许JIT使用更准确的中间表示 - 例如原始数据为64位时为80位 - 而在其他情况下则不是。当满足以下任何条件时,这可能会导致看到不同的结果:

  • 您的代码略有不同,例如使用局部变量而不是字段,这可以改变值是否存储在寄存器中。 (这是一个相对明显的例子;还有其他更微妙的例子可以影响事物,例如方法中存在try块...)
  • 您正在不同的处理器上执行(我曾经观察过AMD和Intel之间的差异;同一制造商的不同CPU之间也可能存在差异)
  • 您正在执行不同的优化级别(例如,在调试器下或不在调试器下)

从C#5规范部分4.1.6:

可以以比操作的结果类型更高的精度执行浮点运算。例如,某些硬件体系结构支持“扩展”或“长双”浮点类型,其范围和精度比double类型更大,并使用此更高精度类型隐式执行所有浮点运算。只有在性能成本过高的情况下,才能使这种硬件架构以较低的精度执行浮点运算,而不是要求实现失去性能和精度,C#允许更高精度的类型用于所有浮点运算。除了提供更精确的结果外,这几乎没有任何可衡量的影响。但是,在x * y / z形式的表达式中,乘法产生的结果超出双范围,但随后的除法将临时结果带回到双范围内,表达式以更高范围格式计算的事实可能导致产生有限的结果而不是无穷大。

另一答案

乔恩的回答当然是正确的。然而,没有一个答案说过如何确保浮点运算以规范保证的精度完成,而不是更多。

在以下情况下,C#会自动将任何浮点数截断回其规范的32位或64位表示:

  • 你输入了一个冗余的显式转换:x + y可能有x和y作为更高精度的数字然后被添加。但(double)((double)x+(double)y)确保在数学发生之前和之后所有内容都被截断为64位精度。
  • 对类,静态字段,数组元素或解除引用指针的实例字段的任何存储始终都会截断。 (本地商店,参数和临时商店的商店不能保证截断;它们可以被注册。结构的字段可能位于短期池中,也可以注册。)

这些保证不是由语言规范做出的,但实现应该遵守这些规则。 C#和CLR的Microsoft实现。

编写代码以确保浮点运算在C#中是可预测的但是可以完成是一件痛苦的事。请注意,这样做可能会减慢算术速度。

关于这种可怕情况的投诉应该针对英特尔,而不是微软;他们是设计芯片的人,这使得可预测的算法变得更慢。

另请注意,这是一个经常被问到的问题。您可以考虑将其作为以下内容的副本:

Why differs floating-point precision in C# when separated by parantheses and when separated by statements?

Why does this floating-point calculation give different results on different machines?

Casting a result to float in method returning float changes result

(.1f+.2f==.3f) != (.1f+.2f).Equals(.3f) Why?

Coercing floating-point to be deterministic in .NET?

C# XNA Visual Studio: Difference between "release" and "debug" modes?

C# - Inconsistent math operation result on 32-bit and 64-bit

Rounding Error in C#: Different results on different PCs

Strange compiler behavior with float literals vs float variables

另一答案

不,不是的。每个CPU的计算结果可能不同,因为浮点运算的实现可能因CPU制造商或CPU设计而异。我甚至还记得一些英特尔处理器中的浮点运算中的一个错误,这搞砸了我们的计算。

然后,JIT编译器如何评估代码存在差异。

另一答案

坦率地说,我不希望同一代码库中的两个位置为x/y返回相同的xy相同的东西 - 在同一台机器上的相同过程中;它可以取决于xy如何通过编译器/ JIT进行优化 - 如果它们以不同的方式注册,它们可以具有不同的中间精度。使用比预期更多的位(寄存器大小)完成了许多操作;并且当它被强制降低到64位时可能会影响结果。 “确切时间”的选择取决于周围代码中发生的其他事情。

另一答案

理论上,在符合IEEE 754的系统上,具有相同输入值的相同操作应该产生相同的结果。

正如维基百科总结它:

IEEE 754-1985允许实现中的许多变化(例如某些值的编码和某些异常的检测)。 IEEE 754-2008已经加强了其中的许多,但仍然存在一些变化(特别是对于二进制格式)。可再现性条款建议语言标准应提供编写可再现程序的方法(即,在语言的所有实现中产生相同结果的程序),并描述为实现可重现结果需要做些什么。

但是,与往常一样,理论与实践不同。常用的大多数编程语言(包括C#)不严格符合IEEE 754,并且不一定提供编写可再现程序的方法。

此外,现代CPU / FPU使得确保严格的IEEE 754合规性有些尴尬。默认情况下,它们将以“扩展精度”运行,在内部存储比位数更多的位。如果需要严格的语义,则需要将FPU中的值拉入CPU寄存器,检查并处理各种FPU异常,然后在每个FPU操作之间将值推回。由于这种尴尬,严格的一致性会降低性能,即使在硬件级别也是如此。 C#标准选择了更“邋”的要求,以避免对较小的变化不成问题的更常见情况施加性能损失。

这一点在实践中通常都不是问题,因为大多数程序员已经内化了(错误的,或者至少是误导性的)浮点数学不精确的想法。另外,我们在这里讨论的错误都非常小,足以使它们因从十进制转换引起的更常见的精度损失而相形见绌。

另一答案

除了其他答案,它甚至可以在同一台机器上使用x/y != x/y

x86中的浮点计算使用80位寄存器完成,但在存储时截断为64位。因此,可以计算,截断第一个除法,然后将其加载回内存并与非截断的除法进行比较。

有关更多信息,请参阅here(它是一个C ++链接,但推理是相同的)

以上是关于浮点运算是否稳定? [重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何将浮点矩阵作为 2D 纹理传递给片段着色器?

在 C++ 中对浮点变量执行算术运算时是不是总是需要使用浮点文字?

定点数学比 armv7-a 上的浮点运算快吗?

按位或运算符 | C中用于对齐内存块的用法[重复]

从 FLOPS(浮点运算)数估算功耗?

我可以在近协议中使用浮点吗?