【问题标题】:atan2f gives different results with m32 flagatan2f 使用 m32 标志给出不同的结果
【发布时间】:2017-06-21 10:04:46
【问题描述】:

我正在将一些代码从 32 位移植到 64 位,并确保答案相同。这样做时,我注意到 atan2f 在两者之间给出了不同的结果。

我创建了这个最小再现:

#include <stdio.h>
#include <math.h>

void testAtan2fIssue(float A, float B)
{
    float atan2fResult = atan2f(A, B);
    printf("atan2f: %.15f\n", atan2fResult);

    float atan2Result = atan2(A, B);
    printf("atan2: %.15f\n", atan2Result);
}

int main()
{
    float A =  16.323556900024414;
    float B = -5.843180656433105;
    testAtan2fIssue(A, B);
}

当构建时:

gcc compilerTest.c -m32 -o 32bit.out -lm

它给出:

atan2f: 1.914544820785522
atan2: 1.914544820785522

当构建时:

gcc compilerTest.c -o 64bit.out -lm

它给出:

atan2f: 1.914544701576233
atan2: 1.914544820785522

请注意,atan2 在两种情况下都给出相同的结果,但 atan2f 不会。

我尝试过的事情:

  1. 使用 -ffloat-store 构建 32 位版本

  2. 使用 -msse2 -mfpmath=sse 构建 32 位版本

  3. 使用 -mfpmath=387 构建 64 位版本

没有人改变我的结果。

(所有这些都是基于它与浮点运算在 32 位和 64 位架构上发生的方式有关的假设。)

问题:

我有哪些选择可以让他们给出相同的结果? (有没有我可以使用的编译器标志?)还有,这里发生了什么?

我在 i7 机器上运行,如果有帮助的话。

【问题讨论】:

  • 使用"atan2f: %.15f\n" 打印可能不够精确。使用"%.16e\n" 或更好的"%a\n" 进行调查。
  • 为什么您希望两个不同的库提供完全相同的结果,以及在两个平台上哪个大小具有float?不同之处并不是人们会担心的。
  • 请注意,1.914544701576233 和 1.914544820785522 在 ULP 中相差 2 位
  • 几乎没有情况应该依赖两个不同的浮点计算来得到相同的结果。
  • 32 位浮点数只能精确到 6 或 7 位有效数字:不是 15。64 位系统上的 sizeof(float) 是 4 还是 8?

标签: c floating-point 32bit-64bit


【解决方案1】:

这在十六进制表示法中更容易看到。

void testAtan2fIssue(float A, float B) {
    double d = atan2(A, B);
    printf("        atan2 : %.13a %.15f\n", d, d);
    float f = atan2f(A, B);
    printf("        atan2f: %.13a %.15f\n", f, f);
    printf("(float) atan2 : %.13a %.15f\n", (float) d, (float) d);

    float f2 = nextafterf(f, 0);
    printf("problem value : %.13a %.15f\n", f2, f2);
}

// _ added for clarity
        atan2 : 0x1.ea1f9_b9d85de4p+0 1.914544_797857041
        atan2f: 0x1.ea1f9_c0000000p+0 1.914544_820785522
(float) atan2 : 0x1.ea1f9_c0000000p+0 1.914544_820785522
problem value : 0x1.ea1f9_a0000000p+0 1.914544_701576233

这里发生了什么?

从double 到float 的转换可以预期是最佳的,但反正切 函数可能会在各种平台上关闭一些ULP。 1.914544701576233 是下一个较小的 float 值,反映了稍差的反正切计算。


我有哪些选择可以让他们给出相同的结果?

很少。代码可以从已建立的代码库推出您自己的my_atan2()。然而,即使这样也可能存在细微的实现差异。 @stark

相反,请考虑让代码检查能够容忍微小的变化。

【讨论】:

  • 我认为问题是问什么标志可以使这些相同(除了 m32)
  • 谢谢。不幸的是,我正在使用遗留代码库,因此使其能够容忍微小的变化并非易事。唉,我想我得解决这个问题了。
  • @TinyTheBrontosaurus 没错,使用 flag 可能是解决此问题的一种方法。然而,我并没有看到帖子仅限于简单的 gcc 选项更改,而是更加开放。
猜你喜欢
  • 1970-01-01
  • 2014-04-13
  • 1970-01-01
  • 2017-05-21
  • 2017-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多