C中无符号变量的算术

Abh*_*gar 2 c unsigned undefined-behavior twos-complement

如何无符号的字符,例如,从取值-128+127?根据我的理解,最重要的位用于表示数字的符号,而字符的剩余位用于表示数字的大小.现在,7位的最大可能幅度是127,所以范围不应该从?-127+127?怎样才能-128有一个结果?

其次,以下行为背后的位级逻辑是什么

#include <stdio.h>

int main()
{
    signed char x = 127;
    x += 1;
    printf("%i", x);
}
Run Code Online (Sandbox Code Playgroud)

输出:

-128
Run Code Online (Sandbox Code Playgroud)

人们可以看到,x成为-128,但为什么呢?这种行为背后的算法是什么?

Ber*_*rer 5

这基于称为Two's Complement的东西.这里的想法是,给定一些二进制数,它的两个补码将是它的一个补码(翻转所有位)加一个.我们可以看到一个简单的例子,让我们找到两个补码13,我们可以写成0b01101.01101 (flip) -> 10010 (+1) --> 10011

现在,虽然如果我们像往常一样将其解释为二进制数,我们将以19十进制形式读取,但我们必须知道该数字是用二进制补码编写的,以便反转过程并得到前一个数字13.因此,从中我们可以看出,我们已经表示了这样的东西,+13 = 01101-13 = 10011注意到正数以a开头,0并且它与a对称1.当使用这种表示时,这将是一个常数,正数将始终以a开头0,而负数则以a 开头1.值得注意的是,我将一个前缀添加0到我的原始表示中13,为了正确表示它的两个补码,我需要这样做.您可以尝试通过相同的示例,而无需这样做并验证它的必要性.

现在,让我们来看看这样的几个值,

??????????????????????????????????????????????????
? Bits ? Unsigned Value ? Two's Complement Value ?
??????????????????????????????????????????????????
? 011  ? 3              ? 3                      ?
??????????????????????????????????????????????????
? 010  ? 2              ? 2                      ?
??????????????????????????????????????????????????
? 001  ? 1              ? 1                      ?
??????????????????????????????????????????????????
? 000  ? 0              ? 0                      ?
??????????????????????????????????????????????????
? 111  ? 7              ? -1                     ?
??????????????????????????????????????????????????
? 110  ? 6              ? -2                     ?
??????????????????????????????????????????????????
? 101  ? 5              ? -3                     ?
??????????????????????????????????????????????????
? 100  ? 4              ? -4                     ?
??????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它的工作原理与我们之前的预期相同,但您现在可以开始了解您发现的"错误".Two's Complement中4位表示的上限是十进制值3.让我们看看如何-4通过简单地添加1它来实现.3 = 0b011因此3+1 = 0b100,您可以从表格中看到两个补语中的-4(而不是相反4).你的情况是这个确切的问题,但有更多的位.这样的有符号表示是循环的,因此在顶部溢出会产生最低值.让我们来看看你的情况

127 = 0b01111111
127 + 1 = 0b10000000
Run Code Online (Sandbox Code Playgroud)

你可以看到它以a开头1,因此它是负数(!),如果你解决了Two's Complement,你会看到它代表-128(因为下限总是大于上限).

据我所知,并非所有硬件都能以相同的方式实现,Intel,AMD,ARM,据我所知,通用CPU的所有主要架构都在其ALU中使用了两个补码,但有些硬件使用其他技术实现整数的签名,所以从根本上说你所描述的行为是不确定的.另一个有趣的事情是,IEEE的浮点运算标准,实现了基于指数偏差的有符号浮点数.

最后,由于我们在这里讨论C,请注意编译器可以优化未定义的行为,本博客文章中描述了这种优化的一个很好的例子.

  • 二进制补码算法的选择是芯片制造商的决定,通常不是编程语言的决定.也就是说,CPU通常使用二进制补码(最常见的,到目前为止)或一个补码或符号幅度算法来确定算术是否实现.编程语言可以模拟这些算术模式中的任何一种,但只是代价. (2认同)