如果'float'<=INT_MAX为真,那么为什么(int)'float'可能会触发未定义的行为?
示例代码(t0.c):
#include <stdio.h>
#include <limits.h>
#define F 2147483600.0f
int main(void)
{
printf("F %fn", F);
printf("INT_MAX %dn", INT_MAX);
printf("F <= INT_MAX %dn", F <= INT_MAX);
if ( F <= INT_MAX )
{
printf("(int)F %dn", (int)F);
}
return 0;
}
调用:
$ 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
问题:
- 如果
F打印为2147483648.000000,那么为什么F <= INT_MAX是真的? - 在这里避免UB的正确方法是什么?
更新。解决方案:
if ( lrintf(F) <= INT_MAX )
{
printf("(int)F %dn", (int)F);
}
UPD2。更好的解决方案:
if ( F <= nextafter(((float)INT_MAX) + 1.0f, -INFINITY) )
{
printf("(int)F %dn", (int)F);
}
回答
你比较类型的值int类型的值float。运算<=符的操作数需要首先转换为通用类型以评估比较。
这属于通常的算术转换。在这种情况下, type 的值int被转换为 type float。并且因为所讨论的值 (2147483647) 不能完全表示为 a float,所以它会产生最接近的可表示值,在本例中为 2147483648。这与宏表示的常量F转换为的内容相匹配,因此比较为真。
关于Fto 类型的强制转换int,因为 的整数部分F在 an 的范围之外int,这会触发未定义的行为。
C 标准的第 6.3.1.4 节规定了这些从整数到浮点数以及从浮点数到整数的转换是如何执行的:
1当一个实数浮点型的有限值被转换为除 之外的整数类型时
_Bool,小数部分被丢弃(即,该值被截断为零)。如果整数部分的值不能用整数类型表示,则行为未定义。2整数类型的值转换为实浮点类型时,如果被转换的值可以用新类型精确表示,则不变。如果要转换的值在可以表示但不能精确表示的值范围内,则结果是最接近的较高或最接近的较低可表示值,以实现定义的方式选择。如果被转换的值在可以表示的值范围之外,则行为未定义。某些隐式转换的结果可能比新类型所需的范围和精度更大(见 6.3.1.8 和 6.8.6.4)
第 6.3.1.8p1 节规定了通常的算术转换是如何执行的:
首先,如果任一操作数的对应实数类型为
long double,则另一个操作数在不改变类型域的情况下转换为对应实数类型为 的类型long double。否则,如果任一操作数的对应实数类型为
double,则在不改变类型域的情况下将另一个操作数转换为对应实数类型为 的类型double。否则,如果任一操作数的对应实数类型为
float,则在不改变类型域的情况下将另一个操作数转换为对应实数类型为 的类型float。
至于在这种情况下如何避免未定义的行为,如果常量F没有后缀 ie2147483600.0则它具有 type double。此类型可以精确地表示任何 32 位整数值,因此给定值不会四舍五入并且可以存储在int.