使用 gcc 将浮点数转换为 C 中的无符号 int



我正在使用 gcc 来测试浮点数到无符号 int 之间的一些简单转换。

以下代码段给出结果 0。

const float maxFloat = 4294967295.0;
unsigned int a = (unsigned int) maxFloat;
printf("%un", a);

0 被打印(我相信这很奇怪(。

另一方面,以下代码段:

const float maxFloat = 4294967295.0;
unsigned int a = (unsigned int) (signed int) maxFloat;
printf("%un", a);

打印2147483648我相信这是正确的结果。

我得到 2 个不同的结果会怎样?

如果你首先这样做:

printf("%fn", 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=%fn", n);
unsigned int a = (unsigned int) n;
int b = (signed int) n;
unsigned int c = (unsigned int) (signed int) n;
printf("a=%un", a);
printf("b=%dn", b);
printf("c=%un", c);

我得到以下结果与 -O0:

n=4294967296.000000
a=0
b=-2147483648
c=2147483648

而这个与 -O1:

n=4294967296.000000
a=4294967295
b=2147483647
c=2147483647

另一方面,如果n被定义为longlong 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 在类型的范围内;没有发出信号。

假设 IEEE 754 浮点数,则4294967295.0数不能精确存储在float中。它将存储为4294967296.0(即 232(。

进一步假设你的unsigned int有32个值位,这只是一个太大而无法容纳unsigned int,所以转换的结果是根据C标准未定义的 -0是一个"合理"的结果。

在你的第二种情况下,你也有未定义的行为,我没有理论在表示层面上发生了什么。事实上,对于 32 位有符号int来说,这个数字太大了(仍然假设这是你的机器使用的(。


从你问题中的这句话:

打印2147483648我相信这是正确的结果。

我假设你想在内存中看到你的float表示形式。强制转换将转换,因此这不是查看表示的方式。以下代码可以:

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("");
}

在线示例

最新更新