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 double
s.
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.