C 在内部使用哪个补码?

问题描述

我查找的所有内容只是告诉我如何在 C 中进行补运算/计算。

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

解决方法

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

  • 符号位为 0 的对应值被取反(符号和 量级);
  • 符号位的值为 -(2M)(二进制补码);
  • 符号位的值为 -(2M- 1)(一个的补码)。

2 的补码是最常见的。

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

有符号溢出会导致未定义的行为。即,假设不会发生,如果您确实发生了,则无法保证您的程序的行为。

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

,

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

所以当你写

x = -x

编译器可能会生成等价于

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

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

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

当然,大多数时候您不必担心这一点。 (而且大多数时候——现在可以肯定地说所有时间——你正在开发一台使用二进制补码的机器,因为它是最受欢迎的机器。) >

因为它是实现定义的,所以文档应该会告诉你。但我想你总是可以用一段代码凭经验确定它:

#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。

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

,

由于 intfloat 等数据类型的大小取决于您使用的机器,如果 C 标准规定了内部表示,我会感到惊讶。所以我怀疑它依赖于实现。

,

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

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