input 0.1无法相加_你真的知道0.1+0.2为何不等于0.3吗?

打开chrome控制台,给一个特别简单的输入如下:

0.1 + 0.2 // 0.30000000000000004 复制代码

不知道你有没有吃惊,这么简单的一个计算,无论在js中还是在python中,都不是准确的0.3,这是为什么呢?

缘起

要了解这个问题,首先我们需要知道浮点数在计算机中到底是如何进行存储的?不知道你是怎么想的,总之我开始的第一反应就是假设是32位的存储空间,我可能会按照整数的存储方式去想象,比如1-24位是整数位,剩余的8位代表小数,这样可以吗?当然是可以的,但是先考虑下下面的这个问题:

f24a984a2932efe3e1973ddb2301e401.png

想象红色区域是所能放置的数字的最大空间,现在有个问题,当我们想继续加0的时候,发现放不下了,因为空间是有限的,这个时候,我们会怎么办?

36b76e97f0f67c1569381642566dd6a4.png

对,没错,科学计数法,就是我们在学习过程中,如果位数太多,我们一般都会用科学计数法来表示,这样的好处是,书写的位数小,表示的位数多,所以,回到计算机中,32位来表示实数的话,最多能表示多少位?2^32次方个,大约就是40亿,40亿数字很多吗?多,但是和无限多的实数集来比,沧海一粟,不够看的,所以计算机的设计者就要考虑这个问题了,如何让计算放下更多的数字?

真的有“定点数”

还记得上面说的,1-24表示整数位,剩余的表示小数位吗?这种存储方式就叫定点数,1-24位每4位表示一个0~9的数字的话,可以有6位表示整数部分,剩余2位表示小数部分,这样我们可以用32位表示从0到999999.99这样1亿个实数,这种用2进制来表示10进制的方式,叫做BCD编码(Binary-Coded Decimal),比如说8421码,从左往右的权依次是8,4,2,1,等等,有兴趣的可以去了解一下。

“定点数”存在哪些问题

定点数有几个明显的缺点:

  • 占了很大的位数,但是能表示的数字范围却是有限的;
  • 无法同时表示很大的数字和很小的数字

其实究其根本原因,还是这种方式的“有限”限制了它,那么有没有一种方式,可以让32位所能表示的数字,更“无限”一点,更适合我们的诉求?

当然,设计计算机的前辈智慧是无限的~

浮点数是如何表示的

就像使用科学计数法一样,计算机前辈在浮点数的设计中也用了一样的思想,IEEE的标准定义了2个基本的浮点数格式,一个是32位的单精度浮点数,一个是64位的双精度浮点数,也就是float或float32和double或float64这两个数据格式,双精度和单精度的表示形式是差不多的,我们以单精度的作为了解和学习。

be13e26aec52c5691d8a326ce27b5a47.png

分为3部分:

  1. 第一部分是符号位,用s表示,代表正负,要记住的是在浮点数的范围内,所有数字都是有符号的;
  2. 第二部分是指数位,用e表示,代表指数,用8位bit表示的数字范围是0~255,为了同时表示大数和小数,我们把0~255去掉头尾(0,255后面会用到)的1~254去映射到-126~127,这样同时可以表示最大最小数字;
  3. 第三部分是有效数位,用f表示,代表的是有效的数位;

综合上述表示和科学计数法,我们的浮点数就可以表示为公式

(-1)^s * 1.f * 2^e

看完公式有没有发现问题?你会发现,我们这个公式无法表示0,的确,这是一个巧妙的设计,我们用0(8个bit都为0)和255(8个bit都为1)来表示一些特殊的数值,可以认为他们2个是特殊的flag位,比如当e和f都为0的时候,我们就认为这个浮点数是0,看下表:

eb3796aa921b632908f0333645f36c45.png

以0.5为例,0.5的符号位s是0,f也是0,e是-1,

这样(-1)^0 * 1.0 * 2 ^ -1 = 0.5

用32位bit表示就是

s e f 0 0111 1110 0000 ...0 1位 8位 23位 0.5 通过这样的表示方式,可以明显的发现32位所能表示的实数范围是很大的,又因为这种方式创建的实数中小数点的位置是可以”浮动“的,所以也被叫做浮点数,

到这里我们知道了浮点数是怎么存储的了,但是还没解决我们开始的问题,为何0.1+0.2!=0.3,首先我们要知道0.1是怎么存储的:

(-1)^s * 1.f * 2^e = 0.1

求解e

s=0 f=0 e=Math.log2(0.1) // -3.321928094887362

可以看出来这里0.1是算不出来一个准确数字的,从0.1到0.9只有0.5是可以求出一个准确的值的,剩下的都算不出来一个准确的值,这也就是为什么0.1+0.2会导致的精度问题,也就是说浮点数无论是表示还是计算其实都是近似计算,而近似计算就一定会导致一些问题,比如,你希望银行给你存钱以及算利息的时候用浮点数计算吗?当然不希望,否则你的钱算多了还好,算少了岂不是亏大了~

浮点数&二进制

把一个二进制表示的浮点数(0.1001),转为10进制表示,因为小数点后的每一位都表示的是2的-N次方,因此转为10进制就是:

(1 * 2 ^ -1) + (0 * 2 ^ -2) + (0 * 2 ^ -3) + (1 * 2 ^ -4) = 0.5625

可以理解为,对于二进制转十进制来说,从小数点开始,往左就是把2的指数从0开始过一位+1,包括0,往右就是从-1开始依次-1。

把一个10进制的浮点数,转为二进制的话,和整数的二进制表示采用“除以 2,然后看余数”的方式相比,小数部分转换是用一个相似的反方向操作,就是乘以2,然后看是否大于1,如果大于1就记下1并把结果减去1,一直重复操作。

比如,十进制的9.1,小数部分0.1转为2进制的过程为:

398f53ba2292e58d9f4e48dd083bc1ec.png

这是得到一个无限循环的部分”0011“,整数部分9转为二进制就是1001,因此结果就是1001.000110011...

把小数点做移3位,得到一个浮点数的结果是 1.001000110011... * 2 ^ 3

找到我们上面的公式 (-1)^s * 1.f * 2^e 套公式可得到:

s = 0 f = 00100011001100110011 001(到23位后自动舍弃,因为最长只能放23位有效数字)

指数位是3,我们e的范围是1-254 对半分正数和负数,所以127表示0,从127开始加3,得到结果是130,130转为二进制表示结果就是: 1000 0010, 所以得到e=1000 0010, 结果如下:

81814a1a8de7f538f33df2402438ac2e.png

所以最终的二进制表示结果是: 0100 0001 0001 0001 1001 1001 1001 1001

如果我们再把这个浮点数表示换算成十进制, 实际得到的准确值是 9.09999942779541015625。相信你现在应该不会感觉奇怪了。

小心你的“存款”

首先,我们了解一下浮点数的加法计算过程是怎么样的,拿0.5 + 0.125来做计算,首先0.5套用公式计算结果是:

s = 0 有效位1.f = 1.0000... e = -1;

0.125 转换为:

s = 0 有效位1.f = 1.0000... e = -3;

然后,计算口诀是 指数位先对齐(小转大,这里要把e统一为-1), 然后按位相加符号位和有效位,e保持统一后的结果,因此:

符号位s 指数位e 有效位1.f 0.5 0 -1 1.0 0.125 0 -3 1.0 0.125对齐指数位 0 -1 0.01 0.5 + 0.125 0 -1 1.01 结果就是 (-1)^0 * 1.25 * 2^-1 = 0.625;

ps: 为啥是1.25?虽然我们计算得出的是1.01 但是不要忘记计算是通过2进制算的,计算十进制的时候要转回来哦,所以0100000.... 后面都是0不用管,小数部分,从头开始乘以2的-N次别忘了,所以结果就是2^-2 = 0.25 加上整数位的1 就是1.25了~

可以发现,其实浮点数的计算过程,通过一个加法器也是可以实现的,电路成本同样不会很高,但是需要注意一些别的问题:

计算过程中,需要先对齐,但是有效数位的长度是23位,假如有一个很大的数字和一个很小的数字进行相加,然后对齐的过程中,小数被0部位过程中直接溢出了,23位不够用了,就会出现问题,补完后一些有效位被丢掉了,从而导致结果上的误差,两个数的指数位差超过23,比如到2^24位(差不多1600万倍),这2个数相加后,结果就直接是较大数,较小数完全被抛弃了。。。

有些同学会急急忙忙去chrome的控制输入下面的代码:

Math.pow(2, 24) + 0.1 // 16777216.1 复制代码

骗人,结果不是还有0.1吗,别急,小伙伴,js内置的Number是64位的,你可以试试

Math.pow(2, 50) + 0.1 // 1125899906842624复制代码

是不是小数没了?【这种现象也叫大数吃小数】

所以如果银行采用IEEE-754 32位的浮点数计数方法来保管存款的话,假设你是一个大老板,你的账户中有2000万rmb,这个时候你的某一个员工给你打了1块钱,哈哈对不起,银行给算丢了,你的存款是不变的!所以,一般银行啊,电商一类的都会在涉及到钱的时候使用定点数或者整数来计算,避免出现精度丢失的问题,如果你去银行涉及数据库,一定要小心谨慎~

总结

这篇文章我们从浮点数的表示开始,到存储,到转换以及计算过程分析了真实的计算机世界中浮点数到底是怎么运行的,从中也了解了浮点数究竟为何会丢失精度:

  1. 浮点数在存储的时候可能出现不能准确转化为对应2进制的情况
  2. 在计算过程中,又存在大数吃小数的可能,也会导致数据不准确

延伸

精度丢失不是没法解决的,有成熟的方案,不做过多介绍,有兴趣大家可以去研究:

Summation Formula 算法

说明:

文章内容大部分参考自 徐文浩 老师的 「深入浅出计算机组成原理」专栏,加了一些自己的理解做了一个简单的总结,之后还会继续不定时的分享一些自己的所得,如果觉得还不错,点个赞吧~

ps: 有同学可能会问,既然只有0.5可以转为一个准确的数字,为何0.1+0.1没有问题,这个我还没仔细研究,不过我猜想是因为本身计算就是一个计算近似值的过程,因此再得出结果后,如果还在一个近似范围内,就会认为没有误差,超过这个范围,则会认为出现误差了,总之我们可以确认的是计算过程中拿到的确实是一个近似数了,这个也确实是导致一些浮点数计算丢失精度的原因~

有兴趣的话可以到这里查看实际的数字在计算机中存储的具体内容~

b0021b2a9a623e9412117aad48279644.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/381422.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python图像增强_Python图像的增强处理操作示例【基于ImageEnhance类】

本文实例讲述了Python图像的增强处理操作。分享给大家供大家参考,具体如下: python中PIL模块中有一个叫做ImageEnhance的类,该类专门用于图像的增强处理,不仅可以增强(或减弱)图像的亮度、对比度、色度&…

python 二项分布_二项分布的理论基础、应用及Python实践

二项分布是概率统计中非常基础、非常实用的一种分布,可以说它在我们的生活中无所不在。它说明了这样一种现象:在给定的试验次数中,某一结果会发生多少次。比如:这个月有多少天会刮北风?今年有多少天会下雨?…

python网络爬虫_python小知识,基于Python 的网络爬虫技术分析

在现阶段大数据的时代中,想要实现对数据的获取和分析,要先具备足够的数据源,网络爬虫技术就为其数据获取提供了良好的条件,且还能够实现对数据源的目的性采集。在网络爬虫技术应用中,Python 脚本语言的使用十分广泛&am…

maven 整体打包_Spirng boot maven多模块打包不踩坑(示例代码)

本文参考 https://blog.csdn.net/Ser_Bad/article/details/78433340经过实战一次通过。回话不多说,话费不多说,直接上图。项目整体结构:父模块:xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocatio…

使用mysql制作学生成绩单_【实例讲解】快速制作学生成绩单

本文由网友潇之骥授权原创直发今天有位学校的老师找我给她作学生成绩单,给的数据如图1:(图1)需要的效果如下:(图2)讲解:要实现这种效果,只要用【邮件合并】这个功能就可以轻松搞定。要用好【邮件合并】这个功能&#x…

c语言 判断一个图是否全连通_基于云平台的全链路大规模网络连通性检测系统详解...

虚拟网络排查问题困难,传统的traceroute等工具很难起到太大作用,大部分情况下都需要到宿主机、混合云网关上抓包来troubleshooting,耗时又费力。有些场景中包的传送路径比较长(如跨域、混合云等),可能丢包的地方比较多&#xff0c…

java对接华为推送服务_华为HMS Core 4.0版本即将来袭:包含新推送服务

据官方消息,在12月27日的HUAWEI Developer Groups(简称HDG)大连活动上,华为消费者云服务HMS运营经理透露HMS Core 4.0版本即将上线,由原来的14项增加至24项核心开放功能,近日即将开放新版本众测,不久之后开发者们将会体…

python xml etree_python解析xml文件之xml.etree.cElementTree和xml.etree.ElementTree区别和基本使用...

1、解析速度:ElementTree在 Python 标准库中有两种实现。一种是纯 Python 实现例如 xml.etree.ElementTree ,另外一种是速度快一点的 xml.etree.cElementTree 。你要记住: 尽量使用 C 语言实现的那种,因为它速度更快,而…

CISSP的成长之路(二十一):用户持有凭证

在51CTO安全频道特别策划的CISSP的成长之路系列的上一篇文章《讲解身份验证过程》里,J0ker给大家介绍了用户验证时所依赖的三种验证要素、密码和密码短语及其安全使用原则。作为技术实施难度和成本最低的用户验证方案,基于密码的用户验证方案广泛应用于我们的日常生…

独家:Havok 发布新的 AI 中间件

独家:Havok 发布新的 AI 中间件 March 23rd, 2009 Alex J. Champandard 2009-3-23,赖勇浩(http://blog.csdn.net/lanphaday)译 原文地址:http://aigamedev.com/middleware/havok-ai-announced 在20日(上周五…

mysql查询入门_Mysql入门(四)查询

我们继续查询~条件查询及逻辑运算符条件查询如果我只想查询&#xff0c;年级小于30的&#xff0c;可以加个where条件关键字~条件查询敲黑板select * from yingxiong where y_age<30where 表示条件(条件关键字)&#xff0c;在where之后跟过滤的条件注意&#xff1a;逻辑运算符…

maven provided_Maven 教程之 pom.xml 详解

点击上方“Java知音”&#xff0c;选择“置顶公众号”技术文章第一时间送达&#xff01;作者&#xff1a;dunwuhttps://github.com/dunwu/blog推荐阅读(点击即可跳转阅读)1. SpringBoot内容聚合2. 面试题内容聚合3. 设计模式内容聚合4. Mybatis内容聚合5. 多线程内容聚合简介什…

python将一个列表里面的某类元素取出来_03|Python列表常见操作

欢迎关注pythoner派微信公众号及头条号Python常见的数据结构我们在上一节中已经讲过再阅读文章前&#xff0c;请打开PythonIDE列表&#xff1a;1.定义一个列表现在我们有3瓶不同类型的啤酒&#xff0c;现在我们将其放入列表之中beer [粉象,白熊,橙色炸弹]2.向列表中追加元素现…

使用SDL打造游戏世界之入门篇 - 2

VC6下SDL的安装和初步使用首先&#xff0c;我们为所有的工程创建一个文件夹tutorial,将下载的开发库SDL-devel-1.2.8-VC6.zip拷贝到tutorial下并解压&#xff0c;并保证如下的文件夹层次&#xff08;图2&#xff09;如下&#xff1a;图2下面我们打开Visual Studio6.0&#xff0…

python虚拟环境管理工具_Python虚拟环境和包管理工具Pipenv的使用详解--看完这一篇就够了...

前言Python虚拟环境是一个虚拟化&#xff0c;从电脑独立开辟出来的环境。在这个虚拟环境中&#xff0c;我们可以pip安装各个项目不同的依赖包&#xff0c;从全局中隔离出来&#xff0c;利于管理。 传统的Python虚拟环境有virtualenv&#xff0c;使用pip freeze → requirements…

centos 6.8安装git_RPM包的卸载与安装,包含依赖包卸载

一、 rpm包的管理介绍&#xff1a;一种用于互联网下载包的打包及安装工具&#xff0c;它包含在某些Linux分发版中&#xff0c;它生成具有RPM扩展名的文件&#xff0c;RPM是RedHat Package Manager&#xff08;RedHat软件包管理工具&#xff09;的缩写&#xff0c;类似windows的…

anaconda matplotlib 输出动画_Python+Matplotlib 制作排序算法的动画

1 、算法的魅力深刻研究排序算法是入门算法较为好的一种方法&#xff0c;现在还记得4年前手动实现常见8种排序算法&#xff0c;通过随机生成一些数据&#xff0c;逐个校验代码实现的排序过程是否与预期的一致&#xff0c;越做越有劲&#xff0c;越有劲越想去研究&#xff0c;公…

如何在centos中找到安装mysql_centos上如何安装mysql

centos可以使用yum安装mysql 但是版本很低&#xff0c;且不灵活。本文将介绍如何使用安装包安装mysql将下载文件放在/opt/mysoft文件夹中解压文件tar -xf MySQL-5.6.22-1.linux_glibc2.5.x86_64.rpm-bundle.tar这里我们要安装mysql的服务端和客服端&#xff0c;所以使用下面两个…

html选择器_css的9个常用选择器

1.类选择器&#xff08;通过类名进行选择&#xff09;<!DOCTYPE html> <html> <head><title></title> </head> <style type"text/css">.p1{color: #00ff00;}.p2{color: #0000ff;} </style> <body><p class…

Resharper4.5:增强你的.net开发

Resharper4.5:增强你的.net开发 介绍 无庸置疑&#xff0c;ReSharper是最智能化的微软Visual Studio插件。它包括一系列丰富的能大大增加C#和Visual Basic.net开发者生产力的特征。使用ReSharper&#xff0c;你可以进行深度代码分析&#xff0c;智能代码协助&#xff0c;实时错…