64位浮点移植问题(64 bit floating point porting issues)

2019-06-24 19:51发布

我是从32位移植我的应用程序到64位。 目前,该代码两种架构下的编译,但结果是不同的。 由于种种原因,我使用花车代替双打。 我认为有从浮到双发生一些隐含的上变频一台机器上,而不是其他。 有没有办法来控制这一点,或特定的陷阱我应该寻找?

编辑补充:

32位平台

 gcc (GCC) 4.1.2 20070925 (Red Hat 4.1.2-33)
 Dual-Core AMD Opteron(tm) Processor 2218 HE

64位平台

 gcc (Ubuntu 4.3.3-5ubuntu4) 4.3.3
 Intel(R) Xeon(R) CPU

运用-mfpmath = 387会有一些帮助,经过1个迭代算法的值是相同的,但除此之外,他们再次出现不一致的现象。

我还要补充一点,我关心的不是,结果是不相同的,那就是移植到64位平台已经发现了一个32位的依赖,而我不知道。

Answer 1:

没有内在需要和双精度浮点数来表现不同的32位和64位代码之间,但往往他们做的。 在回答你的问题将是平台和编译器特定的,所以你需要说你是从什么移植平台,您要移植到什么平台。

在Intel x86平台32位代码经常使用的x87协处理器指令集和浮点寄存器堆的最大兼容性而在amb64 / x86_64的平台上,SSE *指令和XMM寄存器*和经常使用来代替。 这些具有不同精度的特点。

帖子编辑:

鉴于你的平台,你可能要考虑你的x86_64的构建尝试-mfpmath = 387(默认为i386的GCC),看看这解释了不同的结果。 您可能也想看看所有-fmath- *编译器开关的设置,以确保它们符合你想要在这两个版本。



Answer 2:

你的编译器可能使用SSE操作码来完成大部分的64位平台承担的x86-64其浮点运算的,而对于兼容性的原因,它可能之前所使用的FPU了很多的操作。

SSE操作码提供更大量的寄存器和一致性(值始终保持32位或64位),而FPU可能时使用80位的中间值。 所以,你最有可能从这种改进中间精密度之前受益。 (注意额外的精度可引起如x ==年,但COS(X不一致的结果)!= COS(Y)根据分开计算多远发生!)

您可以尝试,因为你正在用gcc编译使用-mfpmath = 387为您的64位版本,看看你的结果符合您的32个结果,以帮助缩小下来。



Answer 3:

像其他人说的,你没有提供足够的信息来确切说出是怎么回事。 但在一般意义上,似乎你一直指望某种浮点行为,你不应该指望。

99次满分100的问题是,你的地方比较两个浮点数是否相等。

如果问题很简单,你要稍有不同的答案,你需要认识到, 没有一个是“正确的” -某种形式的舍入将要发生,不管你是什么架构。 这是理解你的计算中显著数字,是知道你来了任何值近似到一定程度的事情。



Answer 4:

所述的x87 FPU的80位内部寄存器导致其浮点结果与其它的FPU使用内部64位略有不同(在x86_64等)。 你会得到这些处理器之间不同的结果,除非你不介意被冲洗出来的东西到内存或做其他的“strictfp”招数服用大性能命中。

参见: 截断时,浮点舍入

和: http://docs.sun.com/source/806-3568/ncg_goldberg.html



Answer 5:

在x64,SSE2指令集被使用,而在32位应用程序,所述的x87 FPU往往是默认值。

后者在内部存储在一个80位格式的所有浮点值。 后者使用普通的32位IEEE浮点数。

除此之外,做出了重要的一点是, 你不应该依赖于你的浮点运算是整个架构是相同的

即使您使用的32位建立在两台机器上,还是有没有保证,英特尔和AMD将产生相同的结果。 当然,当他们中的一个运行64位版本,您只能增加更多的不确定性。

依托浮点运算的精确结果将几乎总是一个错误。

在32位版本启用SSE2以及将是一个良好的开端,但再次,不要对浮点代码假设。 总是有精度的损失,这是一个糟糕的主意,认为这种损失是可以预见的,或者说,它可以在CPU的或不同的构建之间进行复制。



Answer 6:

GNU编译器有很多相关的,可能会导致计算,在某些情况下打破浮点数编译器选项。 只要搜索这个页面的“浮动”一词,你会发现它们。



Answer 7:

这真的很难控制很多东西。

一开始,C标准往往要求操作漂浮在“双空间”来完成,并且转换回浮动。

英特尔处理器在他们使用的许多这些操作的寄存器80位精度,然后将它们丢弃到64位时,它的存储到主存储器中。 这意味着,一个变量的值可以没有明显的原因而改变。

您可以使用之类的东西GnuMP如果你真的关心,我敢肯定有一些保证一致的结果,其他库。 大多数/抖动产生的误差量的时间,你需要现实世界的分辨率之下。



Answer 8:

真正困难的部分,得到的是结果的两套都是正确的。 这是不公平的表征变化什么,但“不同。” 也许有增加情感依恋旧的结果...但没有数学理由,更喜欢在64位结果的32个结果。

你有没有考虑改变使用定点数学这个应用程序? 不仅是定点数学跨越芯片,编译器和库的变化稳定,在许多情况下,它比浮点运算快过。

作为一个快速测试,移动从32位系统的二进制到64位系统,并运行它。 然后重建64位系统,32位二进制文​​件的应用程序,并运行。 这可能有助于确定哪些变化率(s)实际上产生不同的行为。



Answer 9:

前面已经提到,作为不同的不应该是一个问题,只要它们都是正确的。 理想情况下,你应该有这样的事情(纯计算一般分为相对容易考营)单元测试。

它基本上是不可能保证在CPU和工具链(编译一个标志已经可以发生很大的变化)相同的结果,而这已经是很难保持一致。 强大的设计浮点代码是一项艰巨的任务,但幸运的是,在许多情况下,精度是不是一个问题。



Answer 10:

需要注意的一个重要的事情是,C语言原先指定的计算像

float a=b+c+d;

将B,C,和d转换为最长可用浮点类型(这正好是型double ),把它们相加,然后转换结果float 。 这样的语义是简单的编译器和乐于助人的程序员,但有轻微的困难:用于存储数字最有效的格式是不一样的,作为执行计算最有效的格式。 在没有浮点硬件的机器,它的速度更快,以存储作为一个不必然归一化的64位尾数和单独存储的15位指数和符号的值执行计算,然后对存储为64的值进行操作位double必须每一次操作之前进行解压缩,然后归一化后重新包装(即使只待下一个操作立即解压缩)。 有机器保持改善了速度和精度的长格式的中间结果; ANSI C允许对这种类型long double

不幸的是,ANSI C未能提供由可变参数的函数可以指示他们是否希望所有浮点值转换为一个手段long double ,全部转换为double ,或具有floatdouble如通过doublelong double作为long double 。 已经存在这样一个设施,它本来是很容易使其中不会有区分代码doublelong double值。 遗憾的是,缺乏这种功能意味着在系统中doublelong double不同类型的代码确实有关心的区别,以及如果他们不是系统没有。 这也就意味着大量的代码写在那里的类型同样会打破他们都没有系统的系统; 编译器厂商决定最简单的解决是简单地使long double与同义double和不提供任何类型的,可以准确地保持中间计算。

由于具有不可表示的类型进行中间计算是不好的,有些人决定合乎逻辑的做法是对的计算float类型进行float 。 虽然有些情况下,这可能会比使用更快类型某些硬件平台double ,它往往对精度不良后果。 考虑:

float triangleArea(float a, float b, float c)
{
  long double s = (a+b+c)/2.0;
  return sqrt((s-a)*(s-b)*(s-c)*c);
}

上,其中使用执行中间计算系统long double ,这将产生良好的精度。 上,其中中间计算作为执行系统float ,这可能会产生即使当A,B,和C都精确地表示的可怕的精度。 例如,如果a和b是16777215.0f和c是4.0F,的值s应该是16777217.0,但如果的总和,b和c被计算为float ,这将是1677216.0; 这将产生一个面积不到半正确的值 。 如果a和c分别为16777215.0f和b是4.0F(相同的数字;不同的顺序),则s将得到计算为16777218.0,得到的区域,其是50%的过大

如果您有计算其产生在x86好的结果(许多编译器为它急切地提升为80位类型,即使他们帮倒忙使其无法对程序员),但在x64糟糕的结果,我猜你可能有类似的计算高于该需要具有在比操作数或最终结果的精度较高的执行中间步骤。 改变上述方法的第一行:

  long double s = ((long double)a+b+c)/2.0;

将迫使中间计算中精度更高的要被完成,而不是在低精度执行计算,然后将不准确的结果存储到一个更高精度的变量。



文章来源: 64 bit floating point porting issues