原码、反码、补码,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,加起来是

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也是,转换后存储会丢失精度。

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

参考

www.xiaolincoding.com/os/1_hardwa...

MDN 文档

相关推荐
WeiXiao_Hyy16 分钟前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
吃杠碰小鸡32 分钟前
高中数学-数列-导数证明
前端·数学·算法
kingwebo'sZone38 分钟前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_09011 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农1 小时前
Vue 2.3
前端·javascript·vue.js
夜郎king2 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳2 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵3 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星3 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_3 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js