15

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

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

#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为 a float,但事实证明这会导致浮点值2147483648.0(正!),即使浮点类型完全能够表示-2147483648.0

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

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

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

  • 我正在使用具有 32 位longs 的 C89 编译器,因此任何大于LONG_MAX和小于或等于后缀的值都有一种ULONG_MAX类型Lunsigned long
  • (-2147483648L)实际上是-(见前unsigned long一点)值的一元:-(2147483648L). 此否定操作将值“包装”为的unsigned long2147483648(因为 32 位unsigned longs 具有范围0- 4294967295)。
  • 这个unsigned long数字在打印为 an或传递给函数时看起来像预期的负值,因为它首先被强制转换为 an ,它将这个超出范围的范围包装为(因为 32 位s 的范围为 -2147483648至 2147483647)intintint2147483648-2147483648int
  • 但是,强制转换float为使用实际unsigned long2147483648进行转换,从而产生 的浮点值2147483648.0
4

2 回答 2

13

代替

#define INT32_MIN (-2147483648L)

#define INT32_MIN (-2147483647 - 1)

-2147483648被编译器解释为 的否定2147483648,这会导致 . 的溢出int。所以你应该改写(-2147483647 - 1)
不过,这都是C89标准的。请参阅史蒂夫杰索普的答案C99
通常在long32 位机器上是 32 位,在 64 位机器上是 64 位。int这里把事情做好了。

于 2012-07-18T07:38:51.070 回答
12

在具有 32 位的 C89 中long2147483648L具有类型unsigned long int(请参阅 3.1.3.2 整数常量)。因此,一旦将模算术应用于一元减法运算,INT32_MIN就是类型为 2147483648 的正值unsigned long

在 C99 中,如果大于 32 位,则2147483648L具有类型,否则(请参阅 6.4.4.1 整数常量)。所以没有问题,是负值 -2147483648 类型为or 。longlonglong longINT32_MINlonglong long

long同样在大于 32 位的 C89 中,2147483648L有类型long并且INT32_MIN是负数。

我猜你使用的是 32 位的 C89 编译器long

一种看待它的方法是 C99 修复了 C89 中的“错误”。在 C99 中,没有U后缀的十进制文字始终具有带符号类型,而在 C89 中,它可能是有符号或无符号的,具体取决于其值。

顺便说一句,您可能应该做的是包含limits.h并使用aINT_MIN的最小值intLONG_MINa 的最小值long。它们具有正确的值预期的类型(INT_MINis an int, LONG_MINis a long)。如果您需要一个精确的 32 位类型,那么(假设您的实现是 2 的补码):

  • 对于不必移植的代码,您可以使用您喜欢的正确大小的任何类型,并断言它是安全的。
  • 对于必须可移植的代码,请搜索stdint.h适用于 C89 编译器的 C99 标头版本,并从中使用int32_t和使用INT32_MIN
  • 如果一切都失败了,请写下stdint.h自己,并使用 WiSaGaN 答案中的表达式。它的类型为intifint至少为 32 位,否则为long.
于 2012-07-18T08:16:49.937 回答