前言

有两个问题一直让我疑惑,第一个计算机存储整数为什么用反码来表示?第二个js中0.1+0.2为什么等于0.30000000000000004,计算机小数是如何存储的?

计算机的原码反码以及补码

对于整数来说第一个代表符号位(0为正1为负),其余为表示数字

例如:对于一个8位的二进制来说可以表示范围是:11111111~01111111 = (-1)(1*2^6 + 1*2^5....+1*2^0) ~ (+1)(1*2^6 + 1*2^5....+1*2^0) = (-1)(64 + 32 + 16 + 8 + 4 + 2 + 1) ~ (+1)(64 + 32 + 16 + 8 + 4 + 2 + 1) = -127 ~ +127  在这之间一共有255个数,这也就是一字节能代表的数字最大值和最小值(对应c语言中uint8_t范围),同理可计算int代表四个字节32位时候的范围。

对于正数来说

原码 = 反码 = 补码

例如:我们以8位二进制1来说
原码【00000001】 = 反码【00000001】= 补码【00000001】

对于负数来说

反码 = 原码符号位不变其他位取反
补码 = 反码 + 1

例如:我们以8位二进制-1来说
原码【10000001】= 反码【11111110】= 补码【11111111】

为什么用补码

了解了整数原码反码以及补码计算后我们来分析为什么使用补码。
第一点:
对于0来说科学计数法中只有一个,也就是说+0和-0都是0实际是一个数。那么如果采用原码方式存的话+0 = 00000000,-0 = 10000000,很明显这实际上是一个数另种表示方式(00000000与10000000),这样的话计算机对于0就有了两种存储方式,这是不合理的因为0就有一个。那么如果使用反码来存储+0 = 01111111,-0 = 11111111很明显这对于计算机来说0还是两种存储方式,也是不对的。如果用补码来存储+0 = 0000000,-0 = 00000000,可以看出补码来存储0只有一种,所以计算机采用补码存储。

tips:
对于-0计算补码是这样的首先原码【10000000】,反码【11111111】,在反码基础+1产生进位得到100000000,最高位1是第九位,而我们表示时候是8位,所以1被舍弃就是00000000

第二点:
对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单。计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂, 于是人们想出了将符号位也参与运算的方法。我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了。
计算十进制的表达式: 1-1=0

1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2 : 可以看出原码计算后很明显是错的  1 - 1 = 1 + (-1) = [00000001]反 + [11111110]反 = [11111111]反 = [10000000]原 = -0 : 可以看出计算结果是-0但是对于计算机来说0就是0符号位是没有什么意义的,而且反码对于0还会出现00000000和10000000 两种格式,所以反码不能表示。  1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原 = 0 : 可以看出补码可以解决符号位参与运算以及0唯一的问题

计算机存储小数(float:单精度,double:双精度)

首先我们需要了解整数和小数是如何转成二进制的

对于整数转成二进制

算法:"除2取余,逆序排列"。

例如:10转成二进制:
10 / 2 = 5 --> 余数0
5 / 2 = 2 --> 余数1
2 / 2 = 1 --> 余数0
1 / 2 = 0 --> 余数1
然后逆序排列就是:1010,所以对于10如果用一个字节也就是8位来存储就是:0000 1010

对于小数转成二进制

算法:"乘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.00011001100110011001100110011001100110011001100110011001100110011001.....(这里就是说明小数如何转化成的二进制,计算机存储浮点数时候不是这样存储的)

分析

上面介绍了计算机存储整数,那么计算机如何存储小数的呢?弄清楚了如何存储小数就懂了为什么0.1+0.2 != 0.3了。
根据IEEE754(二进制浮点数算术标准) 浮点标准,任意一个二进制浮点数V可以表示成下面的形式:

V = (-1)^s × M × 2^E(1)(-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。(2)M表示有效数字,大于等于1,小于2。(3)2^E表示指数位。

IEEE 754规定,对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。

对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。

对于M:IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。这样做的目的是可以更加准确保留一位精度。,其中要注意的是,类似四舍五入,如果末位后是1会产生进位。

对于E:我们都知道E指数是有正(小数点左移)有负(小数点右移)的,所以IEEE 754规定,E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。如果一个E值是-4那么计算机真实存储就为1023 + (- 4) = 1019。

下面举例0.1如何在计算机存储的:

js是64位浮点数来存储的,对于0.1(0.000110011001100110011001...)的存储过程是这样的:首先为了保证M的范围需要将0.000110011...右移4位,这个时候M就是1.1001100....,而E就是-4: v = (-1)^0 * 1.1001100..... * 2^(-4) 所以s=0,M=1.1001100.....,E=-4,存储为:
[s]0[E]01111111011[M]1001100110011001100110011001100110011001100110011010
这里存储M的时候我们发现实际上只存储了52位,而0.1的二进制数远远比这个要多,这里就发生了精度的损失。精度丢失第二点是在M产生进位:1001100110011001100110011001100110011001100110011001(前52位) 100110011001.....(52位以后),我们知道M在计算的时候如果第53位如果是1会产生进位,因此产生进位后M为:1001100110011001100110011001100110011001100110011010,很明显这产生进位后的值要比真实值大。

小数二进制转回十进制

最后0.1的存储结果是:[s]0[E]01111111011[M]1001100110011001100110011001100110011001100110011010,那么转回十进制: 1. 补1(M舍弃了起始位的1,所以要补回来):1.1001100110011001100110011001100110011001100110011010 2. 计算E:E=-4,所以转回十进制左移4位,0.00011001100110011001100110011001100110011001100110011010。 3. 转为十进制: 02^(-1) + 02^(-2) + 02^(-3) + 12^(-4) + 12^(-5) + .....+ 02^(-52) 4. 结果:0.00011001100110011001100110011001100110011001100110011010 => 0.100000000000000005551(这里最终数参考网上计算结果,由于他是使用js计算的所以最终值也是不准的,但是基本可以验证0.1在转为二进制后再转成十进制数值已经发生了改变)

这里可以知道0.1在转成二进制后已经不是0.1了,精度产生了丢失,主要原因有两点,第一点小数转二进制时候位数是无限的而计算机表示是有限的(最高52位),第二点由于计算机表示位数是有限的而且还会产生进位导致精度丢失,所以在计算0.1+0.3的时候自然不会是0.4。

js安全问题

我们知道js是使用双精度(64位)来存储数字的,在浮点数64位中存储数字的位数是52位,那么可以表示最大的数范围就是(-)1111111111 1111111111 1111111111 1111111111 1111111111 11 ~ (+)1111111111 1111111111 1111111111 1111111111 1111111111 11 = -2^53 ~ 2^53 = (-)Math.pow(2, 53) ~ (+)Math.pow(2, 53) = -9007199254740992 ~ +9007199254740992
这里就存在安全性问题,如果浮点数超过了这个范围那么所表达的数就是一个。举个例子:

Math.pow(2, 53) === Math.pow(2, 53) + 1.0 // true

因此浮点数的安全数最大就是:9007199254740991。超过这个数就不安全了,因为都存在多个浮点数与之对应。
在业务中我们也会碰到如何后台java同学返回的是一个long类型的数字,而我们用浮点数去就接收可能就出现这种问题。

总结

  1. 我们了解了原码、反码、补码计算过程,以及计算机为什么使用补码来表示,主要原因是因为:第一点0在存储的时候只有补码能把+0和-0保存为一个值。第二点补码可以让符号位参与运算而且真值不变。
  2. 我们了解了浮点数在计算机存储过程,0.1在转为二进制后精度丢失,以及计算机为什么计算0.1+0.3!=0.4,主要是因为第一点小数转二进制时候位数是无限的而计算机表示是有限的(最高52位),第二点由于计算机表示位数是有限的而且还会产生进位导致精度丢失。
  3. 可以看出凡是采用IEEE754标准存储浮点数的都会产生精度丢失(c/c++语言都是采用这个标准)
  4. 浮点数表示和计算解决办法虽然不断有新的数据类型(例如js的BigInt)以及一些第三方库(math.js 、big.js )可以借鉴,但是个人感觉还是转成整数来表示和计算比较好,这样给计算机带来的计算量会小很多。

参考

http://www. ruanyifeng.com/blog/201 0/06/ieee_floating-point_representation.html https:// zh.wikipedia.org/wiki/I EEE_754 https:// juejin.im/post/5b372f10 6fb9a00e6714aa21 https://www. cnblogs.com/zhangziqiu/ archive/2011/03/30/ComputerCode.html https:// segmentfault.com/a/1190 000016586981

分数换算小数补0法_计算机存储整数和小数相关推荐

  1. 分数换算小数补0法_小学数学概念+知识点顺口溜汇总+常用单位换算汇总

    小学数学概念 年月日 一三五七八十腊(12月), 三十一天永不差; 四六九冬(11月)三十日; 平年二月二十八, 闰年二月把一加. 100以内的质数口诀 2.3.5.7和11, 13后面是17, 19 ...

  2. 分数换算小数补0法_四年级下册—小数的意义

    (以下填写教学设计内容,请不要出现参赛选手姓名和班级) 一.课题:小数的意义 二.教学目标: 知识与技能目标:理解和认识小数的意义,以及理解小数和分数之间的联系,认识每相邻两位计数单位的进率 过程与方 ...

  3. 分数换算小数补0法_一年级数学0基础的全过来,最全知识点及基本方法,包你数学不补课都90+...

    我要分享 很多同学对于数学都一种畏惧心理,认为他很难,我肯定学不好它,但其实数学的学习是枯燥但是又是非常有趣的,枯燥的是你每天只能面对无聊的公式,而有趣地方就是你用这些公式可以巧妙灵活的解决很多数学难 ...

  4. 分数换算小数补0法_分数怎么化成整数 分数转化方法

    2020-10-23 13:19:18文/樊越 很多同学都学过分数和整数,那么分数可以转化成整数吗?小编整理了一些分数转化的方法,大家一起来看看吧. 分数和整数的转化 分数是不可以完全化成整数的,严格 ...

  5. 分数换算小数补0法_北师大版六年级数学上册第四单元百分数知识点、练习

    知识点 1.百分数的意义 像84%,28%,2.5%--这样的数叫作百分数,表示一个数是另一个数的百分之几.百分数也叫百分比.百分率.百分数只表示两个数之间的关系,不能带单位名称,它表示的是一个比值. ...

  6. 分数换算小数补0法_小学数学常用公式大全(单位换算表) 长度单位换算【建议收藏】...

    长度单位换算 1千米=1000米    1米=10分米    1分米=10厘米 1厘米=10毫米    1米=100厘米   1米=1000毫米 面积单位换算 1平方千米=100公顷=1000000平 ...

  7. 分数换算小数补0法_数学 | 循环小数的循环节以及永远追不上的乌龟

    今天带学生做练习,遇到一个循环小数的问题: 题目:0.0909090-的循环节是(  ). A.09       B.90       C.090      D.9090 不少学生选择B项90,习题答 ...

  8. 分数换算小数补0法_高考志愿填报时“线差法”和“位次法”哪个好?

    在高考志愿填报中,位次法和线差法是非常常见的两种方法. 线差法又分为"考生线差"与"院校线差" 个人成绩超过相应批次控制线的分数,也就是我们通常所说的" ...

  9. 计算机专业英语被动语态举例,第七讲被动语态的译法_计算机专业英语教程

    第七讲被动语态的译法_计算机专业英语教程 第七讲 被动语态的译法 (Translation of Passive Voice) 被动语态在英语中,特别是科技英语中运用得非常广泛,而汉语的被动句则用得较 ...

最新文章

  1. WMI CIM studio无法连接解决 在XP下wmi取不到值可巧用wmic取值
  2. Vivado无法双击打开xpr工程文件的解决办法
  3. batchnorm pytorch_GitHub趋势榜第一:TensorFlow+PyTorch深度学习资源大汇总
  4. WordPress后台的文章、分类,媒体,页面,评论,链接等所有信息中显示ID并将ID设置为第一列...
  5. JUnit 5 –设置
  6. LeetCode 680. 验证回文字符串 Ⅱ
  7. 小猿圈之Python开发的技巧一?
  8. 基于C#的房屋租赁管理系统设计与实现
  9. 股票 - - 常用指标【上】
  10. 互联网+药店推荐系统的设计与实现
  11. 计算机excel四舍五入,如何在Excel中设置四舍五入
  12. 让你大吃一惊的国外广告联盟
  13. PyTorch源码解读之torchvision.models
  14. Qt中操作SQLite数据库
  15. oracle全文检索
  16. 自媒体视频号博主个人兴趣认证怎么选择?
  17. Oracle定时任务-查询-创建-删除-调用-定时任务时间参数
  18. latex 行间公式大小(批量设置)
  19. 微软电脑管家V2.1公测版正式发布
  20. 斧子演示(AxeSlide):新时期,新用法

热门文章

  1. 计算器算贝塞尔公式_一个强大的高等数学计算器
  2. IDEA一键清理所有无效import
  3. Linux学习总结(63)——CMDB 详细介绍:概念、架构、模型、表设计及开源选择
  4. python xlrd用法_python3 xlrd包的用法
  5. Linux操作文档——Oracle数据库备份与恢复
  6. android 4.0中修改默认输入法
  7. 老前辈分享:使用python-opencv读取视频,计算视频总帧数及FPS的实现
  8. 积分体系运营中,积分兑换比例设计切实可行的两个建议
  9. MD5 加密和解密
  10. 把英文转换成数字python_python实现将英文单词表示的数字转换成阿拉伯数字的方法...