16

Consider this piece of C code:

#include <math.h> #include <stdbool.h> #include <stdio.h> bool foo(int a, int b, int c, int d) { double P = atan2(a, b); double Q = atan2(c, d); return P < Q; } bool bar(int a, int b, int c, int d) { return atan2(a, b) < atan2(c, d); } int main() { if (foo(2, 1, 2, 1)) puts("true"); else puts("false"); if (bar(2, 1, 2, 1)) puts("true"); else puts("false"); return 0; } 

When I compile it with gcc -lm -m64, it prints

false false 

as expected. However, when I compile it with gcc -lm -m32, the output is

false true 

Why does such a trivial difference as storing the result in a variable change the behavior of the code? Is it because the floating point registers are 80-bit? How should I write my code such that weirdness like this is avoided?

(I originally used the variant in bar in a C++ program as a compare function for std::sort. This triggered undefined behavior because comp(a, a) returned true - how should I compare vectors by their angle without UB?)

Comment summary so far

It seems there is consensus that this is because of the differences in where the results are rounded to the range of the double. The C/C++ standards allow this, even when the results differ as a consequence.

A workaround of comparing the input as a * d < b * c, which would indeed help in this example. However, I seek to avoid this category of issues in the future, and not just fix the bug and move on. The issue could still manifest if the inputs themselves were doubles.

I accept that the comparison might be inaccurate due to the nature of floating-point arithmetic, however if the angles are this close, I still want to achieve the strict weak ordering required by std::sort, such that no UB happens.

46
  • 3
    What Every Computer Scientist Should Know About Floating-Point Arithmetic Commented Jul 13, 2020 at 16:40
  • 8
    @JesperJuhl not related... Commented Jul 13, 2020 at 16:42
  • 5
    @SebastianHoffmann this is C, atan2 for floats is called atan2f. I specifically switched away from C++ when I was debugging this to rule this out. Commented Jul 13, 2020 at 16:47
  • 4
    Why is this tagged as C++ if the code is C? Commented Jul 13, 2020 at 16:48
  • 8
    Seeing as the only difference in the asm is storing and loading the doubles P and Q (the latter only stores and loads one of them), it seems that this must have to do with the precision difference between the registers and an actual double. godbolt.org/z/fd79vG Commented Jul 13, 2020 at 16:49

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.