C:将最小32位整数(-2147483648)转换为float给出正数(2147483648.0)

ava*_*zal 14 c floating-point int minimum long-integer

当我遇到一些我觉得奇怪的行为时,我正在研究一个嵌入式项目.我设法在键盘上重现它(见下文)以确认,但我的机器上没有任何其他C编译器可以在它们上进行尝试.

场景:我有一个#define32位整数可以容纳的最负值,然后我尝试使用它来与浮点值进行比较,如下所示:

#define INT32_MIN (-2147483648L)

void main()
{
    float myNumber = 0.0f;
    if(myNumber > INT32_MIN)
    {
        printf("Everything is OK");
    }
    else
    {
        printf("The universe is broken!!");
    }
}
Run Code Online (Sandbox Code Playgroud)

键盘链接:http://codepad.org/cBneMZL5

对我来说,看起来这个代码应该可以正常工作,但令我惊讶的是它打印出来The universe is broken!!.

此代码隐式地将其转换INT32_MIN为a float,但事实证明,这会导致浮点值为2147483648.0(正!),即使浮点类型完全能够表示-2147483648.0.

有没有人对这种行为的原因有任何见解?

代码解决方案:正如Steve Jessop在他的回答中提到的,limits.h并且已经stdint.h包含正确的(工作)int范围define,所以我现在使用这些而不是我自己的#define

问题/解决方案解释摘要:鉴于答案和讨论,我认为这是对正在发生的事情的一个很好的总结(注意:仍然阅读答案/评论,因为它们提供了更详细的解释):

  • 我使用C89编译器具有32位longS,SO大于任何值LONG_MAX且小于或等于ULONG_MAX随后L后缀的类型为unsigned long
  • (-2147483648L)实际上是-一个unsigned long(参见上一点)值的一元:-(2147483648L).此否定操作"包"的值周围成为unsigned long的值2147483648(因为32位unsigned long■找的范围内0- 4294967295).
  • 当它作为一个函数打印或传递给一个函数时,这个unsigned long数字看起来像是预期的负值,因为它首先被转换为一个,它将这个超出范围包裹起来(因为32位s的范围是-2147483648)至2147483647)intintint2147483648-2147483648int
  • 铸到float,但是,使用的实际unsigned long2147483648进行转换,从而产生的浮点值2147483648.0.

WiS*_*GaN 13

更换

#define INT32_MIN (-2147483648L)
Run Code Online (Sandbox Code Playgroud)

#define INT32_MIN (-2147483647 - 1)
Run Code Online (Sandbox Code Playgroud)

-2147483648被编译器解释为否定2147483648,导致溢出int.所以你应该写(-2147483647 - 1).
这都是C89标准的.请参阅Steve Jessop的回答C99.
long典型地是在32位机器32位,和在64位机器64位.int这里完成了事情.

  • 道德是没有负积分文字,只有(无符号)积分文字和一元弊端. (6认同)
  • 更具体地说,编写-2147483648会使编译器将其评估为 - (2147483648).括号中的文字溢出,导致 - ( - 2147483648)= 2147483648.宇宙随后破碎. (4认同)

Ste*_*sop 12

在C89中有32位long,2147483648L有类型unsigned long int(见3.1.3.2整数常量).因此,一旦将模运算应用于一元减运算,INT32_MIN则为正值2147483648 unsigned long.

在C99中,2147483648L类型longif long大于32位,long long否则(参见6.4.4.1整数常量).所以没有问题,INT32_MIN是负值-2147483648类型longlong long.

类似地,在C89中,long大于32位,2147483648L具有类型longINT32_MIN为负.

我猜你正在使用32位的C89编译器long.

一种看待它的方法是C99修复了C89中的"错误".在C99中,没有U后缀的十进制文字总是具有签名类型,而在C89中,它可以是有符号或无符号的,具体取决于其值.

你应该做什么,顺便说一下,包括limits.h和使用a INT_MIN的最小值int,以及LONG_MINa的最小值long.它们具有正确的值预期的类型(INT_MIN是a int,LONG_MIN是a long).如果你需要一个精确的32位类型(假设你的实现是2的补码):

  • 对于不必是可移植的代码,您可以使用您喜欢的任何类型的正确大小,并断言它是安全的.
  • 对于必须可移植的代码,搜索stdint.h适用于C89编译器的C99标头版本,并使用int32_tINT32_MIN从中使用.
  • 如果所有其他方法都失败了,请stdint.h自己写一下,并使用WiSaGaN答案中的表达式.它的类型intif int至少是32位,否则long.