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

Posted

技术标签:

【中文标题】C:将最小 32 位整数 (-2147483648) 转换为浮点数给出正数 (2147483648.0)【英文标题】:C: Casting minimum 32-bit integer (-2147483648) to float gives positive number (2147483648.0) 【发布时间】:2012-07-17 04:39:52 【问题描述】:

当我遇到一些我认为奇怪的行为时,我正在做一个嵌入式项目。我设法在键盘上重现它(见下文)以确认,但我的机器上没有任何其他 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 隐式转换为float,但事实证明这导致浮点值2147483648.0(正数!),即使浮点类型完全能够表示@ 987654329@.

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

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

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

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

【问题讨论】:

fwiw,如果你使用 gcc 4.2.1,宇宙并没有被破坏。 (所以你对编译器的看法可能是对的。) 您使用的是 32 位系统吗?请注意 - 是一个运算符。它不是整数文字的一部分。 无法使用 gcc 4.2.1 重现。你用的是什么编译器? 我可以用 gcc 3.1 重现这个。是的,我知道这有点老了 :-) 在 32 位系统上使用 gcc 4.6 gcc -std=c90 会产生观察到的行为,gcc -std=c99 会“按预期”工作。 另见此问题***.com/questions/9941261/…,其中包含一些标准讨论。 【参考方案1】:

替换

#define INT32_MIN (-2147483648L)

#define INT32_MIN (-2147483647 - 1)

-2147483648 被编译器解释为对2147483648 的否定,这会导致int 溢出。所以你应该改写(-2147483647 - 1)。 这都是C89 标准。请参阅 Steve Jessop 对C99 的回答。 此外,long 在 32 位机器上通常是 32 位,在 64 位机器上是 64 位。 int 在这里完成任务。

【讨论】:

更具体地说,写入 -2147483648 会导致编译器将其评估为 -(2147483648)。括号中的文字溢出,导致 -(-2147483648) = 2147483648。然后宇宙破碎了。 道德是没有负整数文字,只有(无符号)整数文字和一元减号。 仅在两个补码的位模式上争论,它最终应该是否定的。可能是优化过程干扰了这里。 注意long 类型的泛化 - 在 64 位系统(例如 Win64)上并不总是 64 位。 @MichaelBurr 你是对的。这是典型的情况,不是很确定。我稍后会编辑它。【参考方案2】:

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

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

类似地,在 C89 中,long 大于 32 位,2147483648L 的类型为 longINT32_MIN 为负数。

我猜你正在使用带有 32 位 long 的 C89 编译器。

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

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

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

【讨论】:

我不知道。感谢您提供标准。 小笨蛋,最好明确说明In C99, 2147483648L has type long long 也假定为32 位long 有趣且信息丰富,limits.h(我能找到的第一个在线资源...)使用#define INT_MIN (-2147483647 - 1) 机制recommended by WiSaGaN。 很棒的信息,感谢您提供的信息。我正在为 MSP430(嵌入式项目)使用 TI CGT v3.2.2,但我不确定这是 C89 还是其他东西。我现在正在为我的定义使用limits.h 标头,所以也感谢这个提示。正如我在对另一个答案的评论中所说的那样,我完全错过了这种情况,因为我使用这个定义没有问题(将它分配给变量,将它传递给函数,打印出来等),直到我尝试转换它浮动。感谢您的帮助!

以上是关于C:将最小 32 位整数 (-2147483648) 转换为浮点数给出正数 (2147483648.0)的主要内容,如果未能解决你的问题,请参考以下文章

为啥 -(-2147483648) = - 2147483648 在 32 位机器中?

C语言int的取值范围

基本数据类型

int 最大值

python 基础 7 数据类型

数据类型