4

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?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Zaroth
  • 568
  • 7
  • 19
  • Does this answer your question? [When casting floats to integers, overflow causes undefined behavior.](https://stackoverflow.com/questions/526070/handling-overflow-when-casting-doubles-to-integers-in-c) – John Kugelman Feb 18 '22 at 15:19
  • Partially, yes. But I thought the point of undefined behavior is that it differs between different compilers (and implementations of the language spec), not between the compiler and its generated runtime. – Zaroth Feb 18 '22 at 15:23
  • 3
    That's implementation defined behavior. Undefined behavior is completely unpredictable. It's worse than random. It's so devious it often acts like it's doing something predictable just to lull innocent programmers into thinking "it's okay this time" to leave the UB in. See: [Does "Undefined Behavior" really permit *anything* to happen?](https://stackoverflow.com/questions/32132574/does-undefined-behavior-really-permit-anything-to-happen) – John Kugelman Feb 18 '22 at 15:26

0 Answers0