C:将最小32位整数(-2147483648)转换为float给出正数(2147483648.0)
当我遇到一些我觉得奇怪的行为时,我正在研究一个嵌入式项目。 我设法在键盘上重现它(见下文)以确认,但我的机器上没有任何其他C编译器可以在它们上进行尝试。
场景:对于32位整数可以容纳的最负值,我有一个#define
,然后我尝试使用它来与浮点值进行比较,如下所示:
#define INT32_MIN (-2147483648L) void main() { float myNumber = 0.0f; if(myNumber > INT32_MIN) { printf("Everything is OK"); } else { printf("The universe is broken!!"); } }
键盘链接: http : //codepad.org/cBneMZL5
对我来说,看起来这个代码应该可以正常工作,但令我惊讶的是它打印出The universe is broken!!
。
此代码隐式将INT32_MIN
强制转换为float
,但事实certificate,这会导致浮点值为2147483648.0
(正!),即使浮点类型完全能够表示-2147483648.0
。
有没有人对这种行为的原因有任何见解?
代码解决方案 :正如Steve Jessop在他的回答中提到的, limits.h
和stdint.h
包含正确的(工作) int
范围define
s,所以我现在使用这些而不是我自己的#define
问题/解决方案解释摘要:鉴于答案和讨论,我认为这是对正在发生的事情的一个很好的总结(注意:仍然阅读答案/评论,因为它们提供了更详细的解释):
- 我使用的是32位
long
的C89编译器,因此任何大于LONG_MAX
且小于或等于ULONG_MAX
后跟L
后缀的值都有一个unsigned long
类型 -
(-2147483648L)
实际上是一个一元-
在unsigned long
(见上一点)值:-(2147483648L)
。 这个否定操作’包装’值为unsigned long
值2147483648
(因为32位unsigned long
unsigned long
整数的范围为0
–4294967295
)。 - 当这个
unsigned long
int
被打印为int
或传递给函数时,它看起来像预期的负int
值,因为它首先被强制转换为int
,这将这个超出范围的2147483648
包装到-2147483648
(因为32 -bitint
的范围是-2147483648到2147483647) - 但是,转换为
float
会使用实际的unsigned long
整数值2147483648
进行转换,从而导致浮点值为2147483648.0
。
在具有32位long
C89中, 2147483648L
具有unsigned long int
类型(参见3.1.3.2整数常量)。 因此,一旦将模运算应用于一元减运算, INT32_MIN
就是正值2147483648,其类型为unsigned long
。
在C99中,如果long
大于32位,则2147483648L
类型为long long
否则为long long
(参见6.4.4.1整数常量)。 所以没有问题, INT32_MIN
是负值-2147483648,类型为long
或long long
。
类似地,在long
大于32位的C89中, 2147483648L
类型为long
, INT32_MIN
为负。
我猜你使用的是32位long
的C89编译器。
一种看待它的方法是C99修复了C89中的“错误”。 在C99中,没有U
后缀的十进制文字始终具有签名类型,而在C89中,它可以根据其值进行有符号或无符号。
你应该做什么,顺便说一句,包括limits.h
并使用INT_MIN
作为int
的最小值,使用LONG_MIN
作为long
的最小值。 它们具有正确的值和预期的类型( INT_MIN
是int
, LONG_MIN
是long
)。 如果你需要一个精确的32位类型(假设你的实现是2的补码):
- 对于不必是可移植的代码,您可以使用您喜欢的任何类型的正确大小,并声明它是安全的。
- 对于必须可移植的代码,搜索适用于C89编译器的C99头文件
stdint.h
的版本,并使用int32_t
和INT32_MIN
。 - 如果所有其他方法都失败了,请自己编写
stdint.h
,并使用WiSaGaN答案中的表达式。 如果int
至少为32位,则其类型为int
,否则为long
。
更换
#define INT32_MIN (-2147483648L)
同
#define INT32_MIN (-2147483647 - 1)
编译器将-2147483648
解释为2147483648
的否定,这会导致int
溢出。 所以你应该写(-2147483647 - 1)
。
这是所有C89
标准。 请参阅Steve Jessop对C99
的回答。
在32位机器上通常也是32位,在64位机器上通常是64位。 int
在这里完成了事情。