Sample code (t0.c):
#include <stdio.h>
#include <limits.h>
#define F 2147483600.0f
int main(void)
{
printf("F %f\n", F);
printf("INT_MAX %d\n", INT_MAX);
printf("F <= INT_MAX %d\n", F <= INT_MAX);
if ( F <= INT_MAX )
{
printf("(int)F %d\n", (int)F);
}
return 0;
}
Invocations:
$ gcc t0.c && ./a.exe
F 2147483648.000000
INT_MAX 2147483647
F <= INT_MAX 1
(int)F 2147483647
$ clang t0.c && ./a.exe
F 2147483648.000000
INT_MAX 2147483647
F <= INT_MAX 1
(int)F 0
Questions:
- If
Fis printed as2147483648.000000, then whyF <= INT_MAXis true? - What is the correct way to avoid UB here?
UPD. Solution:
if ( lrintf(F) <= INT_MAX )
{
printf("(int)F %d\n", (int)F);
}
UPD2. Better solution:
if ( F <= nextafter(((float)INT_MAX) + 1.0f, -INFINITY) )
{
printf("(int)F %d\n", (int)F);
}
You're comparing a value of type
intwith a value of typefloat. The operands of the<=operator need to first be converted to a common type to evaluate the comparison.This falls under the usual arithmetic conversions. In this case, the value of type
intis converted to typefloat. And because the value in question (2147483647) cannot be represented exactly as afloat, it results in the closest representable value, in this case 2147483648. This matches what the constant represented by the macroFconverts to, so the comparison is true.Regarding the cast of
Fto typeint, because the integer part ofFis outside the range of anint, this triggers undefined behavior.Section 6.3.1.4 of the C standard dictates how these conversions from integer to floating point and from floating point to integer are performed:
And section 6.3.1.8p1 dictates how the usual arithmetic conversions are performed:
As for how to avoid undefined behavior in this case, if the constant
Fhas no suffix i.e.2147483600.0then it has typedouble. This type can represent exactly any 32 bit integer value, so the given value is not rounded and can be stored in anint.