When debugging some C code, I encountered some weird inconsistencies in double calculations. I managed to isolate it to the following minimal example:
#include <math.h>
#include <stdio.h>
int main()
{
float f = 268.6f;
printf("Using variable float %.1f: %d\n", f, (unsigned char)(round(f)));
printf("Using literal float %.1f: %d\n", 268.6f, (unsigned char)(round(268.6f)));
double d = 268.6;
printf("Using variable double %.1lf: %d\n", d, (unsigned char)(round(d)));
printf("Using literal double %.1lf: %d\n", 268.6, (unsigned char)(round(268.6)));
long double ld = 268.6L;
printf("Using variable ldouble %.1llf: %d\n", ld, (unsigned char)(round(ld)));
printf("Using literal ldouble %.1llf: %d\n", 268.6L, (unsigned char)(round(268.6L)));
return 0;
}
Which on my machine after gcc weirdlit.c -lm -oweirdlit; ./weirdlit
using GCC version 11.2.0 produces following output:
Using variable float 268.6: 13
Using literal float 268.6: 255
Using variable double 268.6: 13
Using literal double 268.6: 255
Using variable ldouble 268.6: 13
Using literal ldouble 268.6: 255
While the resulting values are obviously nonsensical (casting floating point values to unsigned char tends to do that), the values are not the point. The thing that baffled me is that the results are different when a literal value is used compared to when a variable with the exact same literal value assigned to it is used. Thinking it can be an optimization quirk, I tested it again with gcc weirdlit.c -lm -O2 -oweirdlit; ./weirdlit
. Turned out I was right, optimization changed the output and this time the results were consistent.
Using variable float 268.6: 255
Using literal float 268.6: 255
Using variable double 268.6: 255
Using literal double 268.6: 255
Using variable ldouble 268.6: 255
Using literal ldouble 268.6: 255
As we can see the result here (255) is the same as when the literals were used in the version without optimization. This shows us that GCC correctly detected that the variables don't change between assignment and being used and the literal values can be substituted in their place.
What I want to know is why this happens. Does GCC use some different versions of the math library functions to calculate the expression values during the optimization than the ones used later at runtime? Or is there some other explanation for this phenomenon?