我正在将我的应用程序从 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 位依赖性。

有帮助吗?

解决方案

有对于浮子没有内在需要和双打表现不同的32位和64位的代码,但它们常常之间。在回答你的问题将是平台和编译器特定的,所以你需要说你是从什么移植平台,你移植到什么平台。

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

发布编辑:

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

其他提示

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

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

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

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

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

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

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

参见: 截断当浮点舍入

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

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

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

除此之外,做出了重要的一点是,您不应该依赖于你的浮点运算是不同的体系结构相同

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

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

在32位版本启用SSE2以及将是一个良好的开端,但再次,不要对浮点代码假设。总有精度的损失,这是一个坏主意假设这个损失是可预测的,或者它可以在CPU的或不同的构建

之间被再现

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

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

有关的开始,C标准往往要求操作,以漂浮在“双空间”进行,并转换回浮动。

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

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

真正困难的部分是 两个都 组结果是正确的。除了“不同”之外,将变化描述为任何事物是不公平的。也许对旧结果的情绪依恋增加了……但是没有数学上的理由比64位结果更喜欢32位结果。

您是否考虑过对此应用程序使用定点数学进行更改?定点数学不仅在芯片、编译器和库的变化中保持稳定,而且在许多情况下它也比浮点数学更快。

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

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

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

需要注意的一件主要事情是,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. 。虽然在某些硬件平台上这可能比使用 type 更快 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,但如果 a、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;

将强制中间计算以更高精度完成,而不是以低精度执行计算,然后将不准确的结果存储到更高精度的变量中。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top