我正在使用 gcc 来测试 float 到 unsigned int 之间的一些简单转换。
下面这段代码给出的结果是 0。
const float maxFloat = 4294967295.0;
unsigned int a = (unsigned int) maxFloat;
printf("%u\n", a);
Run Code Online (Sandbox Code Playgroud)
0 被打印(我认为这很奇怪)。
另一方面,以下代码:
const float maxFloat = 4294967295.0;
unsigned int a = (unsigned int) (signed int) maxFloat;
printf("%u\n", a);
Run Code Online (Sandbox Code Playgroud)
打印 2147483648 我相信这是正确的结果。
如果我得到 2 个不同的结果会发生什么?
如果你首先这样做:
\n\nprintf("%f\\n", maxFloat);\nRun Code Online (Sandbox Code Playgroud)\n\n您将得到的输出是这样的:
\n\n4294967296.000000\nRun Code Online (Sandbox Code Playgroud)\n\n假设 afloat被实现为 IEEE754 单精度浮点类型,则该类型无法准确表示值 4294967295.0,因为没有足够的精度位。最接近的值是 4294967296.0。
假设 an int(同样unsigned int) 是 32 位,则值 4294967296.0 超出了这两种类型的范围。当值无法用给定的整数类型表示时,将浮点类型转换为整数类型会调用未定义的行为。
这在第 6.3.1.4 节中有详细介绍。C 标准,该节规定了从浮点类型到整数类型的转换:
\n\n\n\n\n1当实浮点类型的有限值转换为_Bool以外的整数类型时,小数部分将被丢弃(即值被截断为零)。如果整数部分的值不能用整数类型表示,则行为未定义。61)
\n\n...
\n\n61) 实数浮点类型值转换为无符号类型时,不需要执行整数类型值转换为无符号类型时执行的余数运算。因此,可移植实浮点值的范围是 (\xe2\x88\x921, Utype_MAX+1)。
\n
上述段落中的脚注引用了第 6.3.1.3 节,其中详细介绍了整数到整数的转换:
\n\n\n\n\n1当一个整数类型的值转换为_Bool以外的其他整数类型时,如果该值可以用新的\n类型表示,则该值不变。
\n\n2否则,如果新类型是无符号的,则通过比新类型可以表示的最大值重复加或减 1 来转换该值,直到该值在新类型的范围内。
\n\n3否则,新类型是有符号的,并且值不能在其中表示;结果要么是实现定义的,要么引发实现定义的信号。
\n
当相关值是整数时,您在第一个代码片段中看到的行为与到无符号类型的越界转换一致,但由于要转换的值具有浮点类型,因此是未定义的行为。
\n\n仅仅因为一个实现做到了这一点并不意味着所有实现都会这样做。事实上,如果更改优化设置,gcc 会给出不同的结果。
\n\n例如,在我使用 gcc 5.4.0 的机器上,给出以下代码:
\n\nfloat n = 4294967296;\nprintf("n=%f\\n", n);\nunsigned int a = (unsigned int) n;\nint b = (signed int) n;\nunsigned int c = (unsigned int) (signed int) n;\nprintf("a=%u\\n", a);\nprintf("b=%d\\n", b);\nprintf("c=%u\\n", c);\nRun Code Online (Sandbox Code Playgroud)\n\n我使用 -O0 得到以下结果:
\n\nn=4294967296.000000\na=0\nb=-2147483648\nc=2147483648\nRun Code Online (Sandbox Code Playgroud)\n\n和 -O1 一起使用:
\n\nn=4294967296.000000\na=4294967295\nb=2147483647\nc=2147483647\nRun Code Online (Sandbox Code Playgroud)\n\n如果另一方面n被定义为long或long long,您将始终得到以下输出:
n=4294967296\na=0\nb=0\nc=0\nRun Code Online (Sandbox Code Playgroud)\n\n到无符号的转换由上面的 C 标准明确定义,而到有符号的转换是实现定义的,gcc 定义如下:
\n\n\n\n当值无法在该类型的对象中表示时,将整数转换为有符号整数类型的结果或由此引发的信号(C90 6.2.1.2、C99 和 C11 6.3.1.3)。
\n\n为了转换为宽度为 N 的类型,该值会以 2^N\n 为模减少到该类型的范围内;没有发出任何信号。
\n
小智 2
假设 IEEE 754 浮点数,该数字4294967295.0无法精确存储在float. 它将被存储为4294967296.0(即 2 32)。
进一步假设您unsigned int有 32 个值位,这只是太大而无法放入 中unsigned int,因此根据 C 标准,转换的结果是未定义的 - 这0是一个“合理”的结果。
在第二种情况下,您也有未定义的行为,并且我不知道在表示级别上发生了什么。事实上,这个数字对于 32 位有符号的来说太大了(int仍然假设这是您的机器使用的)。
从你的问题中的这句话来看:
打印 2147483648 我相信这是正确的结果。
我猜你想看看你在记忆中的表现float。转换将转换value ,因此这不是查看表示的方式。下面的代码可以做到:
int main(void) {
const float maxFloat = 4294967295.0;
unsigned char *floatBytes = &maxFloat;
for (int i=0; i < sizeof maxFloat; ++i)
{
printf("0x%02x ", floatBytes[i]);
}
puts("");
}
Run Code Online (Sandbox Code Playgroud)