atan2f 使用 m32 标志给出不同的结果

atan2f gives different results with m32 flag

我正在将一些代码从 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 位版本

None 为我更改了结果。

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

问题:

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

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

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

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

what is happening here?

doublefloat 的转换可以预期是最佳的,但是 arctangent 函数可能有一些 ULP 关闭各种平台。 1.914544701576233 是下一个较小的 float 值,反映了略差的反正切计算。


What are my options for getting them to give the same result?

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

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