将最小 32 位整数 (-2147483648) 转换为浮点数会得到正数 (2147483648.0)

问题描述 投票:0回答:2

我正在开发一个嵌入式项目,当时我遇到了一些我认为很奇怪的行为。我设法在键盘上重现它(见下文)来确认,但我的机器上没有任何其他 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
(正数!),即使浮点类型完全能够表示
-2147483648.0
.

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

代码解决方案:正如 Steve Jessop 在他的回答中提到的,

limits.h
stdint.h
已经包含正确的(工作)
int
范围
define
,所以我现在使用这些而不是我自己的
#define

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

  • 我使用的是具有 32 位
    long
    的 C89 编译器,因此任何大于
    LONG_MAX
    且小于或等于
    ULONG_MAX
    后跟
    L
    后缀的值都具有
    unsigned long
  • 类型
  • (-2147483648L)
    实际上是
    -
    (参见上一点)值
    unsigned long
    上的一元
    -(2147483648L)
    。此否定操作将值“包装”为
    unsigned long
    2147483648
    值(因为 32 位
    unsigned long
    的范围为
    0
    -
    4294967295
    )。
  • 这个
    unsigned long
    数字 看起来 就像预期的负
    int
    值一样,当它被打印为
    int
    或传递给函数时,因为它首先被转换为
    int
    ,这将其包装出来-范围
    2147483648
    -2147483648
    (因为 32 位
    int
    的范围是 -2147483648 到 2147483647)
  • 但是,转换为
    float
    时,使用实际的
    unsigned long
    2147483648
    进行转换,从而得到
    2147483648.0
    的浮点值。
c floating-point integer long-integer minimum
2个回答
13
投票

更换

#define INT32_MIN (-2147483648L)

#define INT32_MIN (-2147483647 - 1)

-2147483648
被编译器解释为
2147483648
的否定,这会导致
int
溢出。所以你应该写
(-2147483647 - 1)
来代替。
但这都是
C89
标准。请参阅 Steve Jessop 的回答
C99

此外,
long
在 32 位计算机上通常为 32 位,在 64 位计算机上通常为 64 位。
int
事情就在这里完成。


12
投票

在具有 32 位

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

在 C99 中,如果

2147483648L
大于 32 位,则
long
具有类型
long
,否则为
long long
(请参见 6.4.4.1 整数常量)。所以没有问题,
INT32_MIN
是负值-2147483648,类型为
long
long long

类似地,在

long
大于 32 位的 C89 中,
2147483648L
具有类型
long
并且
INT32_MIN
为负数。

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

long

一种看待它的方式是,C99 修复了 C89 中的一个“错误”。在 C99 中,没有

U
后缀 always 的十进制文字具有有符号类型,而在 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
© www.soinside.com 2019 - 2024. All rights reserved.