3

I'm porting some code from 32 bit to 64 bit, and ensuring the answers are the same. In doing so, I noticed that atan2f was giving different results between the two.

I created this min repro:

#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);
}

When built with:

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

it gives:

atan2f: 1.914544820785522
atan2: 1.914544820785522

When built with:

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

it gives:

atan2f: 1.914544701576233
atan2: 1.914544820785522

Note that atan2 gives the same result in both cases, but atan2f does not.

Things I have tried:

  1. Building the 32 bit version with -ffloat-store

  2. Building the 32 bit version with -msse2 -mfpmath=sse

  3. Building the 64 bit version with -mfpmath=387

None changed the results for me.

(All of these were based on the hypothesis that it has something to do with the way floating point operations happen on 32 bit vs 64 bit architectures.)

Question:

What are my options for getting them to give the same result? (Is there a compiler flag I could use?) And also, what is happening here?

I'm running on an i7 machine, if that is helpful.

powerss
  • 108
  • 1
  • 4
  • Prinitng with `"atan2f: %.15f\n"` _may be_ insufficiently precise. Use `"%.16e\n"` or better `"%a\n"` for investigation. – chux - Reinstate Monica Feb 03 '17 at 23:06
  • Why do you expect two different libraries to provide exactly the same results and which sizes has `float` on both platforms? The differences are not that one would worry. – too honest for this site Feb 03 '17 at 23:06
  • Note that `1.914544701576233` and `1.914544820785522` differ by 2 bits in the [ULP](https://en.wikipedia.org/wiki/Unit_in_the_last_place) – chux - Reinstate Monica Feb 03 '17 at 23:08
  • There are almost no circumstances where you should depend on two different floating point calculations giving the same result. – stark Feb 03 '17 at 23:09
  • 32 bit floats are only accurate to 6 or 7 significant figures: not 15. What is sizeof(float) on the 64-bit system - 4 or 8? – cup Feb 03 '17 at 23:14
  • "options for getting them to give the same result?" --> Report `printf("%d\n", FLT_EVAL_METHOD);` for each system. That _should not_ make a difference here, but does shed some light on what is happening. – chux - Reinstate Monica Feb 03 '17 at 23:15

1 Answers1

1

This is easier to see in hex notation.

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?

The conversion from double to float can be expected to be optimal, yet arctangent functions may be a few ULP off on various platforms. The 1.914544701576233 is the next smaller float value and reflects the slightly inferior arctangent calculation.


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

Few. Code could roll your own my_atan2() from an established code base. Yet even that may have subtle implementation differences. @stark

Instead, consider making code checking tolerant of the minute variations.

Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • I think the question is asking what flag can make these identical(other than m32) – TinyTheBrontosaurus Feb 04 '17 at 00:10
  • Thanks. Unfortunately I'm working with a legacy code base, so making it tolerant of the minute variations is nontrivial. Alas, guess I'll have to work around the issue. – powerss Feb 04 '17 at 20:28
  • @TinyTheBrontosaurus True, using a _flag_ may have been one approach to solve this problem. Yet I did not see the post as being limited to a simple gcc option change, but more open. – chux - Reinstate Monica Feb 05 '17 at 21:29