使用 gcc 将 C 中的 float 转换为 unsigned int

use*_*954 6 c gcc

我正在使用 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 个不同的结果会发生什么?

dbu*_*ush 7

如果你首先这样做:

\n\n
printf("%f\\n", maxFloat);\n
Run Code Online (Sandbox Code Playgroud)\n\n

您将得到的输出是这样的:

\n\n
4294967296.000000\n
Run Code Online (Sandbox Code Playgroud)\n\n

假设 afloat被实现为 IEEE754 单精度浮点类型,则该类型无法准确表示值 4294967295.0,因为没有足够的精度位。最接近的值是 4294967296.0。

\n\n

假设 an int(同样unsigned int) 是 32 位,则值 4294967296.0 超出了这两种类型的范围。当值无法用给定的整数类型表示时,将浮点类型转换为整数类型会调用未定义的行为

\n\n

这在第 6.3.1.4 节中有详细介绍。C 标准,该节规定了从浮点类型到整数类型的转换:

\n\n
\n

1当实浮点类型的有限值转换为_Bool以外的整数类型时,小数部分将被丢弃(即值被截断为零)。如果整数部分的值不能用整数类型表示,则行为未定义。61)

\n\n

...

\n\n

61) 实数浮点类型值转换为无符号类型时,不需要执行整数类型值转换为无符号类型时执行的余数运算。因此,可移植实浮点值的范围是 (\xe2\x88\x921, Utype_MAX+1)。

\n
\n\n

上述段落中的脚注引用了第 6.3.1.3 节,其中详细介绍了整数到整数的转换:

\n\n
\n

1当一个整数类型的值转换为_Bool以外的其他整数类型时,如果该值可以用新的\n类型表示,则该值不变。

\n\n

2否则,如果新类型是无符号的,则通过比新类型可以表示的最大值重复加或减 1 来转换该值,直到该值在新类型的范围内。

\n\n

3否则,新类型是有符号的,并且值不能在其中表示;结果要么是实现定义的,要么引发实现定义的信号。

\n
\n\n

当相关值是整数时,您在第一个代码片段中看到的行为与到无符号类型的越界转换一致,但由于要转换的值具有浮点类型,因此是未定义的行为。

\n\n

仅仅因为一个实现做到了这一点并不意味着所有实现都会这样做。事实上,如果更改优化设置,gcc 会给出不同的结果。

\n\n

例如,在我使用 gcc 5.4.0 的机器上,给出以下代码:

\n\n
float 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);\n
Run Code Online (Sandbox Code Playgroud)\n\n

我使用 -O0 得到以下结果:

\n\n
n=4294967296.000000\na=0\nb=-2147483648\nc=2147483648\n
Run Code Online (Sandbox Code Playgroud)\n\n

和 -O1 一起使用:

\n\n
n=4294967296.000000\na=4294967295\nb=2147483647\nc=2147483647\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果另一方面n被定义为longlong long,您将始终得到以下输出:

\n\n
n=4294967296\na=0\nb=0\nc=0\n
Run Code Online (Sandbox Code Playgroud)\n\n

到无符号的转换由上面的 C 标准明确定义,而到有符号的转换是实现定义的,gcc 定义如下:

\n\n
\n

当值无法在该类型的对象中表示时,将整数转换为有符号整数类型的结果或由此引发的信号(C90 6.2.1.2、C99 和 C11 6.3.1.3)。

\n\n

为了转换为宽度为 N 的类型,该值会以 2^N\n 为模减少到该类型的范围内;没有发出任何信号。

\n
\n

  • @user3523954 这是未定义行为的另一个例子。因为标准在这种情况下没有强加任何要求,所以实现可以随时自由地做任何它想做的事情,并且不必准确记录它正在做什么。 (2认同)

小智 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)

网上例子