今天碰到一个取反符号,在之前代码中从来没用过,然后有个小哥提出这个计算题,心血来潮就算一算,顺便记录一下。
且以Java中int类型为例解释算式。
- 0x99 十六进制数 99
- 0x66 十六进制数 66
- ~ 按位取反
^按位异或
int类型为32位,其中最高位是符号位,0为正数,1为负数,负数使用补码表示。补码=反码+1。
0x99的二进制表示:
0000 0000 0000 0000 0000 0000 1001 1001
按位取反后:
1111 1111 1111 1111 1111 1111 0110 0110
(此时看最高位为1,发现这是一个负数,如何将这个负数换为十进制且容后再看)
0x66的二进制表示:
0000 0000 0000 0000 0000 0000 0110 0110
将这两个二进制数做异或运算,需要先明白异或的运算法则:同得0,异得1。
数1:1111 1111 1111 1111 1111 1111 0110 0110
数2:0000 0000 0000 0000 0000 0000 0110 0110
结果:1111 1111 1111 1111 1111 1111 0000 0000
可以看到结果是0xFFFFFF00。
至于这个数对应十进制是几,需要换算一次。
从最高位可以看到这是负数,所以这个二进制码是补码,先确定一个负号,然后再来计算这个数的绝对值。
补码和原码的关系是,原码取反+1=补码,补码取反+1=原码。
先来按前一个关系计算:
补码:1111 1111 1111 1111 1111 1111 0000 0000
减一:1111 1111 1111 1111 1111 1110 1111 1111
取反:0000 0000 0000 0000 0000 0001 0000 0000
然后按后一个关系计算:
补码:1111 1111 1111 1111 1111 1111 0000 0000
取反:0000 0000 0000 0000 0000 0000 1111 1111
加一:0000 0000 0000 0000 0000 0001 0000 0000
可以看到两种计算方法结果是相同的,这也是计算机中补码和原码的意义所在,二者存在这种关系使得换算思维变得一致,而非常见的加减运算的逆运算关系。
最后可以看到这个数的绝对值为2的8次方,即256。十六进制表示是0x100,因此这个数可以写作-0x100。
那么,~0x99^0x66 = 0xffffff00 = -0x100 = -256。
接下来代码验证:
public static void main(String[] args) {
int a = 0x99;
int b = 0x66;
int c = ~a^b;
System.out.println(Integer.toHexString(c));
System.out.println(c);
int d = 0xffffff00;
System.out.println(d);
int e = -0x100;
System.out.println(e);
}
附图输出结果:
有更快的计算方式请留言,本喵比较老实,不会技巧。
谢谢阅读。