使用gcc将C中的float转换为带符号的无符号int

时间:2018-05-02 12:34:48

标签: c gcc

我正在使用gcc来测试float到unsigned int之间的一些简单转换。

以下代码给出结果0。

const float maxFloat = 4294967295.0;
unsigned int a = (unsigned int) maxFloat;
printf("%u\n", a);
打印

0(我相信这很奇怪)。

另一方面,下面是一段代码:

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

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

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

2 个答案:

答案 0 :(得分:4)

如果你第一次这样做:

printf("%f\n", maxFloat);

你得到的输出是:

4294967296.000000

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

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

这在C standard的6.3.1.4节中有详细说明,它规定了从浮点类型到整数类型的转换:

  

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

     

...

     

61)当整数类型的值执行剩余操作   转换为无符号类型时不需要执行值   实际浮动类型转换为无符号类型。因此,范围   便携式实数浮点值为(-1,Utype_MAX + 1)。

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

  

1 当整数类型的值转换为_Bool以外的另一个整数类型时,如果该值可以由new表示   类型,它没有变化。

     

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被定义为longlong long,您将始终获得此输出:

n=4294967296
a=0
b=0
c=0

转换为无符号由上面列出的C标准很好地定义,并且转换为signed是实现定义的,gcc defines如下:

  

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

     

对于转换为宽度N的类型,该值以2 ^ N为模减少   在该类型的范围内;没有信号被提出。

答案 1 :(得分:1)

假设IEEE 754浮点数,则4294967295.0无法准确存储在float中。它将被存储为4294967296.0(即2 32 )。

进一步假设您的unsigned int有32个值位,这只是一个太大而不适合unsigned int的值,因此转换的结果根据C标准未定义 - {{ 1}}是一个“合理的”结果。

在你的第二个案例中,你也有未定义的行为,而且我没有理论在表示层面上发生了什么。事实上,对于32位签名0,数字 太大(仍假设这是您的机器使用的)。

在你的问题中这句话:

  

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

我假设您希望在内存中看到int表示。投射将转换 ,因此这不是查看表示的方式。以下代码可以:

float

online example