原码、反码、补码
原码、反码、补码是为了解决计算机里面的减法问题。计算机CPU里面的运算器只有加法器,没有减法器, 对于减法是通过加负数来计算的。
原码
一个数的二进制表示形式,就是原码
,这个二进制最左边的那一位是它的符号位
,正数的符号位就是0
,负数的符号位就是1
。正数的原码、反码、补码都一样,都是原码本身
。
例如:7和-9,用一个字节(8位)来表示就是 00000111
和 10001001
,也就是7和-9的原码。
反码
反码就是在原码的基础上,最高位即符号位不变,其他位取反
,所以-9的反码是11110110
,7的反码还是 00000111
。
补码
补码 = 反码 + 1
,-9的补码就是11110111
,7的补码还是 00000111
。
计算机里面存储数据存的都是补码,所以是用补码计算
计算一下7+(-9),是用补码来加,7的补码加-9补码是11111110
,也就是-2的补码,再求-2的反码,-2的补码减1,-2的反码为 11111101
,-2的原码就是在反码基础上,除了符号位,其他为取反,也就是 10000010
计算一下7-(-9),也就是7+9,7的补码是00000111
,9的补码是00001001
,加起来是00010000
,就是16的补码,也是16的原码。
为什么有补码
如果CPU计算7-7,因为只有加法器,所以转变成7+(-7)。
先用原码计算,7的原码是00000111
,-7的原码是10000111
,加起来是
text
00000111
+ 10000111
____________
10001110
最后得到原码10001110
转为10进制是-14。
再用反码来计算,7的反码是00000111
,-7的反码是11111000
,加起来是
text
00000111
+ 11111000
____________
11111111
得到的反码是11111111
,转为原码是10000000
,得到-0,00000000
也是0,出现两种原码转为10进制都是0。
用补码来计算,7的补码是00000111
,-7的补码是11111001
,加起来是
text
00000111
+ 11111001
____________
100000000
取后8位跟原码一致,0的原码就是00000000
。
小数进制转换和存储
2进制小数转为10进制
小数部分从左往右,依次*2的-1次方,-2次方,-3次方......,然后相加
二进制小数101.11
转为10进制等于 整数部分:12^2+0 2^1+12^0 加 小数部分:12^-1 + 1*2^-2 ,最终是5.75
10进制小数转为2进制
整数部分除2取余,逆序排列
,直到商为0
小数部分乘2取整,顺序排列
: 将小数部分2,将结果的整数部分表示2进制的项,按顺序排列,将结果的小数部分继续2,...,一直到小数部分为0或者形成循环小数为止。
10进制小数9.75转为2进制, 整数部分转换:
text
9/2 = 商4余 1
4/2 = 商2余 0
2/2 = 商1余 0
1/2 = 商0余 1
倒序取余数,整数部分转为二进制是1001,
小数部分:
text
0.75 * 2 = 1.5 整数1作为二进制项
0.5 * 2 = 1.0 整数1作为二进制项 ,小数部分为0,结束
所以转为二进制小数是1001.11
10进制小数0.125转为2进制,整数部分是0,小数部分:
text
0.125 * 2 = 0.25 整数0作为二进制项
0.25 * 2 = 0.5 整数0作为二进制项
0.5 * 2 = 1.0 整数1作为二进制项,小数部分为0,结束
所以转为二进制小数是0.001
0.1 + 0.2 ≠ 0.3 ?
上面的10进制小数转为二进制小数,没有形成循环,0.1和0.2转为二进制小数会形成循环,
看0.1
text
0.1 * 2 = 0.2 0作为二进制项
0.2 * 2 = 0.4 0
0.4 * 2 = 0.8 0
0.8 * 2 = 1.6 1
0.6 * 2 = 1.2 1
0.2 * 2 = 0.4 0
0.4 * 2 = 0.8 0
0.8 * 2 = 1.6 1
0.6 * 2 = 1.2 1
0.2 * 2 = 0.4 0
0.4 * 2 = 0.8 0
0.8 * 2 = 1.6 1
0.6 * 2 = 1.2 1
......
得到 0.0 0011 0011 0011 0011 ...... ,0011一直无限循环,那计算机是怎么处理的?
JS采用IEEE754标准的双精度(64位)来存储数字(Number类型),也就是为每个数字分配64位的存储空间,以科学计数法存储,科学计数法可以方便数字的表达,方便表示非常大或者非常小的数,并且也方便计算。这64位又分为了三个部分:
- 符号位:只有1位,0表示正数,1表示负数
- 指数位:11位,固定值(1023) + 科学计数法指数实际值
- 尾数位:52位,存放小数点后的数字,超过舍弃,这里导致精度丢失
再看10进制0.1:
10进制小数0.1的二进制是0.0 0011 0011 0011 0011 ......
,再转为科学计数法1.1001100110011...... x 2^(-4)
- 符号位:0
- 指数位:1023+(-4)= 1019,1019的11位二进制是,01111111011
- 尾数位:存科学计数法里面的小数点后52位,其余舍弃,但是规定,如果舍弃的第一位为1,往前进一位。
text
1001100110011001100110011001100110011001100110011001 52位
10011001100110011001100110011001100110011001100110011 53位
1001100110011001100110011001100110011001100110011010 进1位后的52位
所以尾数位里面存 1001100110011001100110011001100110011001100110011010,这里就发生了一次精度丢失
,0.1存在计算机里面一共64位:
text
0 01111111011 1001100110011001100110011001100110011001100110011010
符号位 指数位 尾数位
同样0.2也是,转换后存储会丢失精度。
这两个小数无法用完整的二进制来表示,只能根据精度舍入,所以计算机里只能采用近似数的方式来保存,那两个近似数相加,得到的必然也是一个近似数。