在所有现代PC上,64位浮点数的行为是否相同?

时间:2010-01-27 20:07:23

标签: 64-bit floating-point portability ieee-754

我想知道我是否可以假设在相同的64位浮点数上的相同操作在任何现代PC和大多数常见编程语言中都能得到完全相同的结果? (C ++,Java,C#等)。我们可以假设,我们正在对数字进行操作,结果也是一个数字(没有NaN,INF等)。

我知道使用浮点数(IEEE 854-1987和IEEE 754-2008)有两种非常类似的计算标准。但是我不知道它在实践中是怎么回事。

7 个答案:

答案 0 :(得分:8)

实现64位浮点的现代处理器通常实现接近IEEE 754-1985标准的东西,最近被754-2008标准取代。

754标准规定了从某些基本操作中得到的结果,特别是加法,减法,乘法,除法,平方根和否定。在大多数情况下,精确指定数值结果:结果必须是在舍入模式指定的方向上最接近精确数学结果的可表示数字(最接近,朝向无穷大,朝向零或朝向负无穷大)。在“到最近”模式中,标准还规定了如何破坏关系。

因此,不涉及溢出等异常条件的操作将在符合标准的不同处理器上获得相同的结果。

但是,有几个问题会影响在不同处理器上获得相同的结果。其中之一是编译器通常可以以各种方式自由地实现浮点运算序列。例如,如果在C中写入“a = b c + d”,其中所有变量都声明为double,则编译器可以在双精度算术中计算“b c”,或者使用更多范围或精度。例如,如果处理器具有能够保持扩展精度浮点数的寄存器并且使用扩展精度执行算法不会比使用双精度算术执行任何CPU时间,则编译器可能使用扩展生成代码-精确。在这样的处理器上,您可能无法获得与在其他处理器上相同的结果。即使编译器定期执行此操作,在某些情况下也可能不会因为寄存器在复杂序列期间已满,因此它会将中间结果临时存储在内存中。当它这样做时,它可能只写64位双精度而不是扩展精度数。因此,包含浮点运算的例程可能会给出不同的结果,因为它是使用不同的代码编译的,可能在一个地方内联,并且编译器需要注册其他内容。

某些处理器具有计算乘法和加法的指令,因此可以计算“b c + d”而不进行中间舍入,并获得比首先计算b的处理器更准确的结果< / em> c然后添加d。

你的编译器可能有开关来控制这样的行为。

有些地方754-1985标准不需要独特的结果。例如,当确定是否发生了下溢(结果太小而无法准确表示)时,该标准允许实现在将有效数(分数位)舍入到目标精度之前或之后进行确定。因此,一些实现将告诉您在其他实现不会发生下溢时。

处理器的一个共同特点是具有“几乎IEEE 754”模式,通过替换零而不是返回标准所需的非常小的数字,消除了处理下溢的难度。当然,在这种模式下执行时,与在更兼容模式下执行时相比,您将得到不同的数字。出于性能原因,非兼容模式可能是编译器和/或操作系统的默认设置。

请注意,IEEE 754实现通常不是由硬件提供,而是由硬件和软件的组合提供。处理器可以完成大部分工作,但依靠软件来处理某些异常,设置某些模式等等。

当你超越基本的算术运算到诸如正弦和余弦之类的东西时,你非常依赖于你使用的库。超越函数通常使用精心设计的近似值计算。这些实现由各种工程师独立开发,并且彼此获得不同的结果。在一个系统上,sin函数可以在ULP(精度最小的单位)内为小参数(小于pi左右)提供准确的结果,但对于大参数则更大的错误。在另一个系统中,sin函数可以在几个ULP内为所有参数提供准确的结果。没有当前的数学库可以为所有输入生成正确的舍入结果。有一个项目,crlibm(正确的Rounded Libm),已经为这个目标做了一些很好的工作,他们已经开发了数学库的重要部分的实现,这些部分是正确舍入的并且具有良好的性能,但不是所有的数学库爱好。

总之,如果您有一组可管理的计算,了解您的编译器实现,并且非常小心,您可以在不同的处理器上依赖相同的结果。否则,获得完全相同的结果不是您可以信赖的。

答案 1 :(得分:7)

如果你的意思是得到完全相同的结果,那么答案是否定的。

在某些情况下,您甚至可能在同一台计算机上对调试(非优化)构建与发布构建(优化)获得不同的结果,因此,甚至不要假设结果在不同的计算机上总是相同的。 / p>

(例如,在具有Intel处理器的计算机上,如果优化器为寄存器中的中间结果保留变量,则可能发生这种情况,存储在未优化构建的内存中。由于Intel FPU寄存器为80位,因此变量是64位,中间结果将在优化的构建中以更高的精度存储,从而在以后的结果中产生不同的值。)。

然而,在实践中,您可能经常得到相同的结果,但您不应该依赖它。

答案 2 :(得分:2)

现代FPU都以单格式和双格式实现IEEE754浮点数,有些格式为扩展格式。支持一组特定的操作(几乎math.h中的任何操作),其中有一些特殊指令。

答案 3 :(得分:1)

假设您正在谈论应用多个操作,我认为您不会得到确切的数字。 CPU架构,编译器使用,优化设置将改变计算结果。

如果你的意思是操作的确切顺序(在汇编级别),我认为你仍会得到变化。例如,英特尔芯片在内部使用扩展精度(80位),这可能不是其他CPU的情况。 (我认为不要求扩展精度)

答案 4 :(得分:1)

同一个C#程序可以在同一台PC上显示不同的数值结果,一旦在调试模式下编译而没有优化,第二次在发布模式下编译并启用优化。这是我个人的经历。当我们第一次为我们的一个程序设置一个自动回归测试套件时,我们没有考虑到这一点,并且我们很多测试都没有任何明显的原因而完全感到惊讶。

答案 5 :(得分:1)

对于x86上的C#,使用80位FP寄存器。

C#标准规定处理器必须以与类型本身相同的精度或大于该类型的精度运行(即在'double'的情况下为64位)。除存储外,允许促销。这意味着本地和参数可能大于64位精度。

换句话说,将成员变量分配给局部变量可能(实际上在某些情况下)足以给出不等式。

另请参阅:Float/double precision in debug/release modes

答案 6 :(得分:0)

对于64位数据类型,我只知道IEEE 754中的"double precision" / "binary64"(1985年和2008年对于常见情况没有太大差别)。

注意:IEEE 854-1987中定义的基数类型无论如何都会被IEEE 754-2008取代。