位运算
一、码制
二进制数正负数三种表示法 —— 原码;反码;补码。
注意:二进制数的第一位是符号位,0正 1负;后面是数值位。
正数的原码 = 反码 = 补码,即符号位为0,位于首位,随后是二进制数绝对值数值
例如10的原码为=0000 1010 ;反码=0000 1010;补码=0000 1010;
负数而言,三种表示方法不一样。
原码:符号位“1”+二进制数绝对值数值
反码:符号位“1”+数值位按位取反
补码:反码+“1” (末位)
重:已知一个数的补码求原码,其实就是对该补码再求补码,因为原码和补码互为补数。
二进制反码和补码运算性质:
[[X]反]反=[X]原
[[X]补]补=[X]原
[X]反+[Y]反=[X+Y]反(循环进位)
[X]补+[Y]补=[X+Y]补(舍弃进位)
二、基础运算
1、加法运算
(4 + 7 = 11)
0 0 0 0 0 1 0 0 4
+ 0 0 0 0 0 1 1 1 7
------------------
0 0 0 0 1 0 1 1 11
2、减法运算
需要先把负数转为补码,再相加,例如2+(-10) = -8
原码 反码 补码
+1 00000010 00000010 00000010
-1 10001010 11110101 11110110
0 0 0 0 0 0 1 0
+ 1 1 1 1 0 1 1 0
------------------
1 1 1 1 1 0 0 0 -->结果为-8的补码
再多举几个加减的例子
1、10+(-2)= 8
10 原码 = 0000 1010
(-2) 原码: 1000 0010
(-2) 反码:1111 1101
(-2) 补码:1111 1110
00001010
+ 11111110
------------------
00001000 = 8
2、5 + (-11) = -6
5 原码 = 0000 0101
(-11) 原码: 1000 1011
(-11) 反码:1111 0100
(-11) 补码:1111 0101
0000 0101
+ 1111 0101
------------------
1111 1010 -6的补码
验证一下原码
再取(1111 1010)的补码 = 原码 1000 0110 = -6的原码
3、乘法运算
当其中一个乘数是2^n时,等于将另一个乘数的所有二进制位向左移动n位。
而当两个乘数都不是2n时,这时候编译器会将其中一个乘数分解成多个2n相加的结果,然后进行分别运算。
(0000 1100) 12 x (0000 1000) 8
相当于:
8(2^3)+4(2^2)
(8 << 3) + (8 << 2) = 0110 0000 = 96
4、除法运算
当除数是2^n时,等于将被除数的所有二进制位向右移动n位。
当除数不是2^n时,这个原理比较难并且精度问题。
浮点数简要介绍
由于浮点数的小数位的二进制数据依旧保持2^n的特性,假如10111001属于小数位,那么这部分小数位的值等于:1/2 + 1/4 + 1/8 + 1/16 + 1/128 = 0.9453125。
因此,把一个小数位很多的浮点数(3.14159269453125)存入计算机的时候,所有小数位会被拆解成很多的2^n进行保存。由于位数总是有限的,因此当分解到第n位数溢出时,就会出现精度偏差。另一方面,过多的分解计算势必要消耗大量的时钟周期,这也是大量的浮点数运算容易引发卡顿的原因,比如cell滑动时动态算高。(此处原理未来再具体分析)。#future
5、位运算符
(1)&(按位与):对应两个二进制位均为1时,结果才为1,否则为0。
(2)|(按位或):对应两个二进制位只要有一个为1时,结果就为1,否则为0。
(3)^(按位异或):对应两个二进制位都不相同时,结果才为1,否则为0。(不同为1,相同为0)
(4)~(取反):所有二进制位都变为相反的(0变1,1变0),符号位也变。
(5)>>(右移):右移n位本质是a=a/2^n。把整数a的所有二进制位全部右移n位,符号位不变。(正数)符号位为0时,数值最高位补0。(负数)符号位为1时,数值最高位补0或1,则取决与编译系统,一般最高位补符号位。
(6)<<(左移):左移n位本质是a=a*2^n。把整数a的所有二进制位全部左移n位,溢出的最高位丢弃,包括符号位,低位补0。因此左移会改变正负性。