I am wondering what might explain a floating-point difference that I observed today. Is it a bug, or tripping some undefined behavior? Below is the code. My hope is to understand the behavior so that I might make it consistent across compilers, processors and platforms.
#include <stdio.h>
#include <math.h>
#define BUG_ON_GCC
#ifdef BUG_ON_GCC
__attribute__((noinline))
#else
inline __attribute__((always_inline))
#endif
double safe_pow(
double x,
double y)
{
printf("x = %llx\n", *((unsigned long long *)&x));
printf("y = %llx\n", *((unsigned long long *)&y));
double result = pow(x, y);
printf("r = %llx\n", *((unsigned long long *)&result));
return result;
}
int main() {
printf("%ld\n", sizeof(pow(15.034465284692086, 3.466120406090667)));
printf("%.24f\n", pow(15.034465284692086, 3.466120406090667));
printf("%ld\n", sizeof(safe_pow(15.034465284692086, 3.466120406090667)));
printf("%.24f\n", safe_pow(15.034465284692086, 3.466120406090667));
return 0;
}
And the results that I see using Clang in a Linux VM:
8
12020.670425990641888347454369
8
x = 402e11a56f0d331e
y = 400bba9d55e142e0
r = 40c77a55d084d419
12020.670425990641888347454369
Using GCC in the same VM:
8
12020.670425990643707336857915
8
x = 402e11a56f0d331e
y = 400bba9d55e142e0
r = 40c77a55d084d419
12020.670425990641888347454369
Using Clang on macOS on the same machine:
8
12020.670425990643707336857915
8
x = 402e11a56f0d331e
y = 400bba9d55e142e0
r = 40c77a55d084d41a
12020.670425990643707336857915
As you can see, there are no calls to pow
, inline or otherwise. gcc
and clang
both compute it at compile time, and they compute it differently. The difference is in the least significant bit.
Neither of your operands is exactly representable as an IEEE double. An infinite-precision calculator suggests that GCC uses the exact operands and calculates the answer with greater precision than double
offers, and then converts the result to double
, while Clang converts the operands to double
losing some precision, and calculates with that. There is no right or wrong behaviour, both are right according to the standard.