如果您首先执行此操作:
printf("%f\n", maxFloat);
你会得到以下输出:
4294967296.000000
假设一个
float
被实现为IEEE754单精度浮点类型,值4294967295.0无法被该类型准确表示,因为精度不够。它能够存储的最接近的值是4294967296.0。
假设一个
int
(以及
unsigned int
)是32位,值4294967296.0超出了这两种类型的范围。当给定整数类型无法表示值时,将浮点类型转换为整数类型会引发
未定义行为。
这在
C标准的第6.3.1.4节中有详细说明,该节规定了从浮点类型到整数类型的转换。
1 当将实浮点类型的有限值转换为除了_Bool之外的整数类型时,小数部分将被丢弃(即向零舍入)。如果整数部分的值无法由整数类型表示,则行为未定义。61)
...
61) 将整数类型的值转换为无符号类型时执行的余数操作在将实浮点类型的值转换为无符号类型时不需要执行。因此,可移植实浮点值的范围为(-1,Utype_MAX + 1)。
上述段落中的脚注引用了第6.3.1.3节,其中详细介绍了整数到整数的转换:
1 当一个整数类型的值被转换为除了_Bool以外的另一种整数类型时,如果该值可以用新类型表示,则它保持不变。
2 否则,如果新类型是无符号的,则通过反复添加或减去比新类型中可以表示的最大值多一的值,直到该值在新类型的范围内进行转换。
3 否则,新类型为有符号类型且该值无法表示;结果要么是实现定义的,要么会引发实现定义的信号。
您在第一个代码片段中看到的行为与将整数类型的值转换为无符号类型时看到的行为一致(当涉及到整数时),但由于要转换的值具有浮点类型,因此这是未定义的行为。
仅因为一个实现这样做并不意味着所有实现都会这样做。 实际上,如果更改优化设置,则gcc会给出不同的结果。
例如,在我的机器上使用gcc 5.4.0,给定以下代码:
float n = 4294967296;
printf("n=%f\n", n);
unsigned int a = (unsigned int) n;
int b = (signed int) n;
unsigned int c = (unsigned int) (signed int) n;
printf("a=%u\n", a);
printf("b=%d\n", b);
printf("c=%u\n", c);
我使用-O0得到以下结果:
n=4294967296.000000
a=0
b=-2147483648
c=2147483648
而使用 -O1 编译选项:
n=4294967296.000000
a=4294967295
b=2147483647
c=2147483647
如果另一方面定义
n
为
long
或
long long
,则始终会得到此输出:
n=4294967296
a=0
b=0
c=0
将有符号整数转换为无符号整数在C标准中已经定义明确,如上所述。而将有符号整数转换为另一种有符号整数类型的结果是由实现定义的,gcc定义如下:
当将整数转换为有符号整数类型时,如果该值无法表示为该类型的对象,则产生的结果或引发的信号(C90 6.2.1.2、C99和C11 6.3.1.3)。
对于宽度为N的类型的转换,该值被模2^N减少以使其处于类型范围内;不会引发任何信号。