原码、反码、补码,0.1 + 0.2≠0.3

原码、反码、补码

原码、反码、补码是为了解决计算机里面的减法问题。计算机CPU里面的运算器只有加法器,没有减法器,
对于减法是通过加负数来计算的。

原码

一个数的二进制表示形式,就是原码,这个二进制最左边的那一位是它的符号位,正数的符号位就是0,负数的符号位就是1正数的原码、反码、补码都一样,都是原码本身

例如:7和-9,用一个字节(8位)来表示就是 0000011110001001,也就是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,加起来是

    00000111
  + 10000111
  ____________
    10001110 

最后得到原码10001110转为10进制是-14。

再用反码来计算,7的反码是00000111,-7的反码是11111000,加起来是

    00000111
  + 11111000
  ____________
    11111111 

得到的反码是11111111 ,转为原码是10000000,得到-0,00000000也是0,出现两种原码转为10进制都是0。

用补码来计算,7的补码是00000111,-7的补码是11111001,加起来是

    00000111
  + 11111001
  ____________
   100000000 

取后8位跟原码一致,0的原码就是00000000

小数进制转换和存储

2进制小数转为10进制

小数部分从左往右,依次*2的-1次方,-2次方,-3次方……,然后相加

二进制小数101.11转为10进制等于 整数部分:12^2+02^1+12^0 加 小数部分:12^-1 + 1*2^-2 ,最终是5.75

10进制小数转为2进制

整数部分除2取余,逆序排列,直到商为0

小数部分乘2取整,顺序排列: 将小数部分2,将结果的整数部分表示2进制的项,按顺序排列,将结果的小数部分继续2,…,一直到小数部分为0或者形成循环小数为止。

10进制小数9.75转为2进制,
整数部分转换:

9/2 = 商4余 1
4/2 = 商2余 0
2/2 = 商1余 0
1/2 = 商0余 1

倒序取余数,整数部分转为二进制是1001,

小数部分:

 0.75 * 2 = 1.5  整数1作为二进制项
  0.5 * 2 = 1.0  整数1作为二进制项 ,小数部分为0,结束

所以转为二进制小数是1001.11

10进制小数0.125转为2进制,整数部分是0,小数部分:

 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

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,往前进一位。
1001100110011001100110011001100110011001100110011001  52位
10011001100110011001100110011001100110011001100110011 53位
1001100110011001100110011001100110011001100110011010  进1位后的52位

所以尾数位里面存 1001100110011001100110011001100110011001100110011010,这里就发生了一次精度丢失,0.1存在计算机里面一共64位:

0 01111111011 1001100110011001100110011001100110011001100110011010
符号位  指数位    尾数位

同样0.2也是,转换后存储会丢失精度。

这两个小数无法用完整的二进制来表示,只能根据精度舍入,所以计算机里只能采用近似数的方式来保存,那两个近似数相加,得到的必然也是一个近似数。

参考

www.xiaolincoding.com/os/1_hardwa…

MDN 文档

原文链接:https://juejin.cn/post/7323135762798624779 作者:陵南

(0)
上一篇 2024年1月14日 下午4:10
下一篇 2024年1月14日 下午4:20

相关推荐

发表回复

登录后才能评论