【问题标题】:Why 2147483648 + 2147483648 = 0 in C?为什么 2147483648 + 2147483648 = 0 在 C 中?
【发布时间】:2020-05-06 00:24:53
【问题描述】:

代码如下:

int a;
a = 2147483648 + 2147483648;
printf("%d", a);

我知道 int 变量的最大数量是 2147483647。 所以,据我所知,2147483648 = -2147483648。 但是为什么 2147483648 + 2147483648 = 0?

【问题讨论】:

  • 嗯,你看,2147483648 是 0x80000000 + 0x80000000 = 0x100000000。这不适合 32 位,所以最后的 1 丢失了,变成了 0。
  • 看看它是如何用二进制表示的,以及计算机是如何将数字相加的。
  • 如果你知道a = -a然后在两边加上a...a+a = -a+a0
  • @BradyDean C 算术是根据值定义的,而不是表示形式

标签: c


【解决方案1】:

2147483648 是 1 后跟 31 个零。如果您添加两次,它只会溢出(所有 32 位都将为零,并且将设置进位)。由于进位基本上被丢弃(忽略,当你将值存储到a中时),你看不到它,你看到的都是0。

10000000 00000000 00000000 00000000 +10000000 00000000 00000000 00000000 ---------------------------------- (1)00000000 00000000 00000000 00000000

【讨论】:

  • “carry 基本被丢弃”不是真的。 2147483648 可能是现代系统上的 64 位值,并且从第 31 位“进位”到第 32 位
  • 好吧,我谈到了 32 位整数。没错,进位在 x64 上进入第 32 位,但是当您尝试将寄存器中的数字存储到内存中时,它确实被 IMO 丢弃了。
  • x86 也有 64 位值。您在评论中说得对,将加法结果存储到 a 可能会丢失位,但您的回答并未反映这一点。添加的结果不会导致任何溢出,并且仍然包含1(在符合 C99 的系统上)。你可以试试printf("%lld", 2147483648 + 2147483648);
【解决方案2】:

因为您的常量不适合int,所以它们被视为long(或者,如有必要,long long,因此:

2147483648 = 0x80000000 + 另一个 0x80000000 = 0x100000000,当您将其分配给 a 时,它会被截断为 0(假设为 4 字节 ints。)

gcc 为该分配发出warning

今天是第二个。

【讨论】:

  • 但是,如果我将 int 更改为 long long,并将 %d 更改为 %lld,它会给我相同的结果。为什么?
  • "被截断为 0" - 这是由实现定义的这里发生的事情(其他可能性包括发出信号)
  • @Hoseong 格式说明符必须匹配a 的类型,即int。截断发生在分配中,而不是 printf
  • @HoseongJeon 这个作品:wandbox.org/permlink/3YLOUDHtMGxbQsBP
  • MSVC 对 2^31 到 2^32-1 范围内的常量应用 C90 规则,即使同时还支持 long long(这不符合任何标准)。或者它曾经是无论如何,我不了解 MSVC 中任何潜在的最新发展
【解决方案3】:

此代码的行为取决于:

  • 您使用的是哪个版本的 C
  • 编译器上的类型大小是多少(实现定义)
  • 也许是其他实现定义的功能

代码可以输出任何数字或引发信号,但这必须包含在编译器的文档中。

代码有两个地方依赖于实现定义的行为:加法的结果,以及将加法的结果存储到int 变量中的操作。

我还想指出,C 算术是基于,而不是表示。答案不取决于 2 的补码或二进制进位或类似的东西。 2147483648 始终是一个大的正整数,它不是负数。两个正数相加也不能产生负数。这通常被误解。


以下是一些示例:

  • long 为32 位的C90 实现中,2147483648 的类型为unsigned intunsigned long。根据无符号算术的定义,结果是 2147483648 + 2147483648 模 2^32 的数学值,结果为0
  • int 是32 位且long 是64 位的实现中,2147483648 的类型为long。那么加法的结果是类型 long 和值 4294967296 。然后,将此值分配给 int 是一个超出范围的分配,导致实现定义的行为。实现定义这一点的一种常见方式是截断高位。发出信号是另一种选择。
  • 在使用 32 位 long 和 64 位 long long 的普通 C99 实现中,情况与前面的项目符号非常相似,只是类型为 long long
  • 可能存在一些带有 33 位 long 的深奥系统,其中的加法随后会由于溢出而导致未定义的行为,但我们通常不担心这一点,并假设没有人会设计这样的系统。 (不过有些系统是 36 位整数的!)
  • MSVC 等不符合标准的编译器当然可以做一些不同的事情。

【讨论】:

  • 好的。我认为你在谈论一些很棒的想法。你能回答here,我刚刚问过MSVC long long吗?
  • 关于“假设没有人会设计这样的系统”:我可以看到有人使用可调整的设置来实现 C 以探索标准、测试代码以发现关于可移植性的错误假设等等,而不是为了使用直接生成生产可执行文件。
  • @phuclv gcc.godbolt.org/z/uPdHYs,哎呀。这不能在 ISO C99 或更高版本中给出4,在 ISO C++11 或更高版本中也不能给出。 (假设 8 位字符)。此外,这是一个 C 问题,您链接到 C++ 一致性页面。
猜你喜欢
  • 2017-07-16
  • 2012-09-19
  • 2020-08-20
  • 1970-01-01
  • 1970-01-01
  • 2021-02-26
  • 2012-06-29
  • 2011-04-11
相关资源
最近更新 更多