补码与符号位取反

补码与符号位取反

先来一个 C 语言的小例子:

#include <stdio.h>
#include <stdint.h>

int main(void)
{
    int16_t n = -1;
    n &= 0x7FFF;        // 按位与
    printf("%d", n);    // 这里输出什么?
    return 0;
}

对于16位的整数 n ,按位与运行将最高位设置为0(符号位),得到的结果却不是 1 ,结果是 32767 。

原因在于有符号整数的实现方式。

有符号整数,最容易想到的方式是在最高位加一个符号位,0表示整数,1表示负数,其它位不变(保留原始值),也即是原码方式。但这个方式有一个问题,存在两个0,正0和负0,在计算时需要先判断符号位,然后才能决定用加法还是减法,机器计算不便。

另外一个方法是负数全部按位取反,也就是反码方式。这个运算就相对简单了,进行加法时,按位计算,0和0为0,0为1为1,1和1为0并产生进位,最高位有进位时,结果要加1,减法可处理为其负数的加法。但还是有点问题,还存在两个0,正0和负0。

问题是出现负数上,那么把负数的反码 + 1 ,不就把负0去掉了吗?还真的是这样,而同时负数比整数能多表示一个数(这是基于同余的)。

严格的表达为:

对于位数为 n 的整数,其补码 [x]补 为:(2^n + x) mod 2^n ,
表示的范围为 -2^(n-1) <= n < 2^(n-1) ,注意正数最大为 2^(n-1)-1
即:
当 0 <= x < 2^(n-1) 时,
[x]补 = x 的原码
当 -2^(n-1) <= x < 0 时,
[x]补 = 2^n + x 的原码。

而经验上,可看作负数的补码为其反码加1(特殊数 -2^(n-1) ) 的反码 :

x < 0 时:
[x]补 = [x]反 + 1
特殊数 [ -2^(n-1) ] = 100...0

它的加法处理非常简单,符号位也可以运行,

[x+y]补 = (2^n + x + y) mod 2^n = ((2^n + x) + (2^n + y)) mod 2^n = [x]补 + [y]补
[x-y]补 = (2^n + x - y) mod 2^n = ... = [x]补 + [-y]补

现在回到原来的问题,对于16位 -1 ,其补码为:

[-1]补 = [-1]反 + 1 = 0xFFFFF

按位与去掉符号位,得到的是 0x7FFF 也就是16位整数最大的正数( 2^15 - 1) 32767。

posted @ 2017-04-19 18:26  drop *  阅读(2558)  评论(0编辑  收藏  举报