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,但为什么呢?这种行为背后的算法是什么?
这基于称为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,请注意编译器可以优化未定义的行为,本博客文章中描述了这种优化的一个很好的例子.