C 在内部使用哪个补码?

Which complement does C use internally?

我查找的所有内容都告诉我如何在 C 中对 operations/calculations 进行补码

我想知道 C 在内部使用什么表示以及它如何处理溢出。

简而言之,我们可以说C中的2s补码定义为C中1的补码与1的补码之和

C中的2s补码是由C中的1s补码生成的。众所周知,二进制数的1s补码是通过将位1转换为0和0而产生的到 1;二进制数的 1 补码加 1 生成二进制数的 2 补码。

C 是使用一个补码、二进制补码还是 sign/magnitude 表示负整数是 实现定义的 。也就是说,每个编译器都可以选择,通常基于它为其生成代码的处理器。

所以当你写

x = -x

编译器可能生成等同于

的代码
x = ~x;            /* one's complement */

x = ~x + 1;        /* two's complement */

x ^= 0x80000000;   /* sign/magnitude, assuming 32 bits */

当然,大多数时候您不必担心这一点。 (同样在大多数时候——如今可以肯定地说 all 的时间——你正在使用二进制补码的机器上工作,因为它是压倒性的最爱。 )

由于它是实现定义的,文档应该会告诉您。但我想你总是可以通过一段代码凭经验确定它:

#include <stdio.h>
#include <limits.h>

int main()
{
    int x = 1;
    int negativex = -x;
    if(negativex == ~x)
         printf("one's complement\n");
    else if(negativex == ~x + 1)
         printf("two's complement\n");
    else if(negativex == (x ^ (1 << (sizeof(x) * CHAR_BIT - 1))))
         printf("sign/magnitude\n");
    else
         printf("what the heck kind of machine are you on??\n");
}

您问的是溢出问题。对于 unsigned 整数,溢出以明显的方式定义为“环绕”(即,它以 2^N 为模执行,其中 N 是位数)。但是对于有符号整数,溢出在形式上是未定义的:理论上可以存在有符号整数溢出产生错误的机器,或多或少类似于除以 0。

(当然,在普通的二进制补码机器上,有符号整数运算也以明显的方式安静地环绕,因为二进制补码的全部意义在于环绕溢出使其工作。)

C 允许有符号整数的 3 种表示 (https://port70.net/~nsz/c/c11/n1570.html#6.2.6.2p2):

  • the corresponding value with sign bit 0 is negated (sign and magnitude);
  • the sign bit has the value -(2M) (two's complement);
  • the sign bit has the value -(2M- 1) (ones' complement).

补码最常见。

无符号溢出环绕无符号的最大值。

签名溢出导致未定义的行为。也就是说,假定它不会发生,如果您确实让它发生了,则无法保证您的程序的行为。

有符号原子中的溢出是一个例外:它定义明确并且那里强制要求二进制补码:https://port70.net/~nsz/c/c11/n1570.html#7.17.7.5p3