可能你还不懂浮点数

在网上看到一个问题

55f66ab4be50f3d0065ff25e8608eed5.png

然后看到这篇关于浮点数的文章,希望大家看了之后有所启发

想一下,为什么第一个打印的和预设值不同,但是第二个是相同的?

f274f59f61ab9725d407c535c871bef7.png

3b97e633bcc8432d1fd3ac18b58155df.png

如图:

尾数部分是如何转变成二进制的?

b014fa2c8dd0ee6411ad041e34c9a810.png

前言

很多人在初学写程式时都会遇到所谓的浮点误差,如果你到目前都还没被浮点误差雷过,那只能说你真的很幸运XD

以下图Python 的例子来说 0.1 + 0.2 并不等于0.38.7 / 10也不等于0.87,而是0.869999…,真的超怪der 🤔

58a9d69db2a38d1db4295428d2a81216.png

但这绝对不是什么神bug,也不是Python 设计得不好,而是浮点数在做运算时必然的结果,所以即便是到了Node.js 或其他语言也都是一样

26ffb81d94ca6ca3c854f24372e00629.png

电脑如何储存一个整数(Integer)

在讲为什么会有浮点误差之前,先来谈谈电脑是怎么用0 跟1 来表示一个 整数,大家应该都知道二进制这个东西:像 101 代表2² + 2⁰ 也就是5、1010代表2³ + 2¹ 也就是10

ae94104f6384a740329488b101daa241.png

如果是一个unsigned 的32 bit 整数,代表他有32 个位置可以放0 或1,所以最小值就是 0000...0000 也就是0,而最大值 1111...1111 代表2³¹ + 2³⁰ + … + 2¹ + 2⁰ 也就是4294967295

从排列组合的角度来想,因为每一个bit 都可以是0 或1,整个变数值有2³² 种可能性,所以可以 精确的 表达出0 到2³²-1 中任一个值,不会有任何误差

浮点数(Floating Point)

虽然从0 到2³²-1 之间有很多很多个整数,但数量终究是 有限 的,就是2³² 个那么多而已;但浮点数就大大的不同了,大家可以这样想:在1 到10 这个区间中只有十个整数,但却有 无限多个 浮点数,譬如说5.1、5.11、5.111 等等,再怎么数都数不完

但因为在32 bit 的空间中就只有2³² 种可能性,为了把所有浮点数都塞在这个32 bit 的空间里面,许多CPU 厂商发明了各种浮点数的表示方式,但若各家CPU 的格式都不一样也很麻烦,所以最后是以IEEE发布的IEEE 754作为通用的浮点数运算标准,后来的CPU 也都遵循这个标准进行设计

IEEE 754

IEEE 754 里面定义了很多东西,其中包括单精度(32 bit)、双精度(64 bit)跟特殊值(无穷大、NaN)的表示方式等等

正规化

以8.5 这个符点数来说,如果要变成IEEE 754 格式的话必须先做正规化:把8.5 拆成8 + 0.5 也就是2³ + 1/2¹,接着写成二进位变成1000.1,最后再写成1.0001 x 2³,跟十进位的科学记号满像的

单精度浮点数

在IEEE 754 中32 bit 浮点数被拆成三个部分,分别是sign、exponent 跟fraction,加起来总共是32 个bit

c55dd38c49f1134962b5e0ec49b2971a.png

  • sign:最左侧的1 bit 代表正负号,正数的话sign 就为0,反之则是 1

  • exponent:中间的8 bit 代表正规化后的次方数,采用的是 超127格式,也就是3 还要加上127 = 130

  • fraction:最右侧的23 bit 放的是小数部分,以1.0001 来说就是去掉1. 之后的0001

所以如果把8.5 表示成32 bit 格式的话就会是这样:

这图我画超久的,请大家仔细看XD

f0f6baa2986ce2eff3114a747b175ca0.png

什么情况下会不准呢?

刚刚8.5 的例子可以完全表示为2³+ 1/2¹,是因为8 跟0.5 刚好都是2 的次方数,所以完全不需要牺牲任何精准度

但如果是8.9 的话因为没办法换成2 的次方数相加,所以最后会被迫表示成1.0001110011… x 2³,而且还会产生大概0.0000003 的误差,好奇结果的话可以到IEEE-754 Floating Point Converter网站上玩玩看

双精度浮点数

上面讲的单精度浮点数只用了32 bit 来表示,为了让误差更小,IEEE 754 也定义了如何用64 bit 来表示浮点数,跟32 bit 比起来fraction 部分大了超过两倍,从23 bit 变成52 bit,所以精准度自然提高许多

107d20ba8ebccb4c11a021c8142dd30a.png

以刚刚不太准的8.9 为例,用64 bit 表示的话虽然可以变得更准,但因为8.9 无法完全写成2 的次方数相加,到了小数下16 位还是出现误差,不过跟原本的误差0.0000003 比起来已经小了很多

600f7ba7e62a6672ca01c787e5d5236d.png

类似的情况还有像Python 中的 1.0 跟 0.999...999 是相等的、123跟 122.999...999 也是相等的,因为他们之间的差距已经小到无法放在fraction 里面,所以就二进制的格式看来他们每一个bit 都一样

3907a5a94f52fc994f73e8c7b0389f95.png

解决方法

既然无法避免浮点误差,那就只好跟他共处了(打不过就加入?),这边提供两个比较常见的处理方法

设定最大允许误差ε (epsilon)

在某些语言里面会提供所谓的epsilon,用来让你判断是不是在浮点误差的允许范围内,以Python 来说epsilon 的值大概是2.2e-16

6b2c25b558a9e6f19773795bcb7e0395.png

所以你可以把 0.1 + 0.2 == 0.3 改写成0.1 + 0.2 — 0.3 <= epsilon,这样就能避免浮点误差在运算过程中作怪,也就可以正确比较出0.1 加0.2 是不是等于0.3

当然如果系统没提供的话你也可以自己定义一个epsilon,设定在2 的-15 次方左右

完全使用十进位进行计算

之所以会有浮点误差,是因为十进制转二进制的过程中没办法把所有的小数部分都塞进fraction,既然转换可能会有误差,那干脆就不要转了,直接用十进制来做计算!!

在Python 里面有一个module 叫做decimal,它可以帮你用十进位来进行计算,就像你自己用纸笔计算0.1 + 0.2 绝对不会出错、也不会有任何误差(其他语言也有类似的模组)

c7c9675703f9f86cf0a441fcd0b2f70b.png

自从我用了Decimal 之后不只bug 不见了,连考试也都考一百分了呢!

虽然用十进位进行计算可以完全躲掉浮点误差,但因为Decimal 的十进位计算是模拟出来的,在最底层的CPU 电路中还是用二进位在进行计算,所以跑起来会比原生的浮点运算慢非常多,所以也不建议全部的浮点运算都用Decimal 来做

总结

回归到这篇文章的主题:「为什么浮点误差是无法避免的?」,相信大家都已经知道了

至于你说知道IEEE 754 的浮点数格式有什么用吗?好像也没什么特别的用处XD,只是觉得能从浮点数的格式来探究误差的成因很有趣而已,感觉离真相又近了一点点

而且说不定哪天会有人问我「为什么浮点运算会产生误差而整数不会」,那时我就可以有自信的讲解给他听,而不是跟他说「反正浮点运算就是会有误差,背起来就对了」

后记

这是我第一次写这种几乎是纯原理的文章,不管你是觉得很有趣,还是觉得太理论了不知道学这要干嘛,都欢迎你在下方留言跟我说,或是透过拍手表达你的意见,这样我也比较能知道你们喜欢什么类型的文章,谢谢大家~

参考资料

  • https://zh.wikipedia.org/wiki/IEEE_754

  • https://www.h-schmidt.net/FloatConverter/IEEE754.html

  • https://zh.wikipedia.org/wiki/%E6%B5%AE%E7%82%B9%E6%95%B0

文章转自:https://medium.com/starbugs/see-why-floating-point-error-can-not-be-avoided-from-ieee-754-809720b32175

ac932bf0bbb975857d2eba7b0b9d526a.png

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

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

相关文章

股票自动交易使用协议

国家的法律规定其它人是不可能代替别人进行股票操作的。所以我们让用户使用股票自动交易软件的时候必须很清楚的让用户知道&#xff0c;他使用这个软件是他自己的意图&#xff0c;软件执行的策略也是它自己的策略&#xff0c;而不是我强加给他的。这样我们就需要写一个协议给用…

RTP协议的封装

最近一段时间学习了RTP协议相关的内容&#xff0c;一方面为了将自己学到的部分记录下来&#xff0c;便于后续查找&#xff0c;另一方面用于记录笔记 一个协议的封装是为了满足协议的功能需求的。从前面提出的功能需求&#xff0c;可以推测出RTP封装中应该有同步源和时戳等字段&…

【收集】ADOADO.NET 读取 Oracle 数据集

开始尝试用存储过程读取Oracle 数据集&#xff0c;收集了一些文章&#xff0c;基本上都来自MSDN&#xff1a;使用 ADO.NET 访问 Oracle 9i 存储过程 http://www.microsoft.com/china/MSDN/library/data/dataAccess/DMSDNorsps.mspx?mfrtrue如果包返回多个游标&#xff0c;则 D…

python 运行shell命令

在python 中实现运行多条shell命令 今天小编就为大家分享一篇在python 中实现运行多条shell命令&#xff0c;具有很好的参考价值&#xff0c;希望对大家有所帮助。 一起跟随小编过来看看吧 使用py时可能需要连续运行多条shell 命令 # coding: UTF-8 import sys reload(sys) sy…

组合公式计算机,(最新整理)排列与组合的概念与计算公式

《(最新整理)排列与组合的概念与计算公式》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《(最新整理)排列与组合的概念与计算公式(3页珍藏版)》请在人人文库网上搜索。1、完整)排列与组合的概念与计算公式(完整)排列与组合的概念与计算公式编辑整理&#xff1a;尊敬的…

sudo应用

sudo需求&#xff0c;公司程序员某些数据同步后需重启服务&#xff0c;给予root免登录权限固然简单&#xff0c;但存在太大的风险&#xff1b;所以我做了sudo限制&#xff0c;只允许用户从固定IP连接执行指定的命令&#xff0c;不需输入用户名 密码技术要点&#xff1a;sudo …

Alpha冲刺(7/10)

团队信息 队名&#xff1a;爸爸饿了组长博客&#xff1a;here作业博客&#xff1a;here组员情况 组员1&#xff08;组长&#xff09;&#xff1a;王彬 过去两天完成了哪些任务 学会了POSTMAN的使用&#xff0c;对后端已经完成的接口进行了收发消息正确性的验证推进项目进度&…

biztalk BLogs

http://biztalkdev.com/blogs/default.aspx http://www.biztalkgurus.com/tags/http/default.aspx http://tag.csdn.net/tag/biztalk.xml http://www.cnblogs.com/team/BiztalkSolution%20.html 转载于:https://www.cnblogs.com/lianyonglove/archive/2007/04/02/697270.html

epoll模型之服务器设计

Linux2.6内核中提高网络I/O性能的新方法-epoll I/O多路复用技术在比较多的TCP网络服务器中有使用&#xff0c;即比较多的用到select函数。1、为什么select落后首先&#xff0c;在Linux内核中&#xff0c;select所用到的FD_SET是有限的&#xff0c;即内核中有个参数__FD_SETSIZE…

服务器分虚拟空间,服务器怎样分虚拟主机

自己操作的话&#xff0c;首先有一个固定ip&#xff0c;做主机的电脑可以24小时开机。装上服务器软件&#xff0c;可以装 IIS 或者 linux/bsdapache。数据库的话是看你的网页设计需要的&#xff0c;如果有数据库开发的动态网页&#xff0c;那就必须装了。最好是专用服务的web服…

AIX 用户管理

http://www.ibm.com/developerworks/cn/aix/library/au-aixuseradmin/

不限学历、不限学校、华为天才少年招聘

我在前同事的朋友圈看到的招聘信息。不限学历&#xff0c;不限学校我相信这个规则一定会让后续的很多企业效仿&#xff0c;工作至今&#xff0c;遇到很多能力很强但是学历一般的人&#xff0c;而对于面试者&#xff0c;可以大胆的说出那句话&#xff0c;人家华为都不限制学校学…

ASP.NET 2.0服务器控件与组件开发中文版即将出版

Professional ASP.NET 2.0 Server Control and Component Development的中文版的书已经翻译成中文了&#xff0c;本书比较厚&#xff0c;不知道会翻译的如何。http://www.china-pub.com/computers/common/info.asp?id34470这本书有些地方还是值得看的&#xff0c;因为是唯一一…

饥荒联机版服务器显示错误,小白求问 搭服务器出现这种情况是怎么回事

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼[00:00:00]: DownloadMods(0)[00:00:00]: FMOD Error: An invalid object handle was used.[00:00:00]: LOADING LUA SUCCESS[00:00:01]: PlayerDeaths could not load morgue[00:00:01]: PlayerHistory could not load player_his…

linux学习第九天 (Linux就该这么学)

今天讲了raid0 至少两块盘串联在一起&#xff0c;读写性能提升&#xff0c;但不具备数据备份和错误修复能力&#xff0c;RAID1把两块盘绑定&#xff0c;在写入数据时&#xff0c;同时写入到多块硬盘设备&#xff0c;raid5推荐使用&#xff0c;10推荐使用 LVM,今天是在外面加班…

[Windows Phone] 为应用添加后台计划任务 – Scheduled Task Agent

前段时间做过一个天气应用&#xff0c;一直是只支持前台获取数据&#xff0c;上周末参加了Windows Phone的CodeJam和高手们交流了一下&#xff0c;发现实现后台定时更新功能也不是很难&#xff0c;于是在网上找一些资料&#xff0c;在找资料的过程中发现&#xff0c;网上的一些…

基于UDP高性能传输协议UDT

UDP详解 一、 概述 UDT是一个高性能的基于UDP的数据传输协议&#xff0c;它是为支持高速广域网上海量数据传输而设计&#xff0c;为解决TCP的效率和公平问题&#xff0c;同时提供可靠的数据流和报文传输。 UDT是C库&#xff0c;几乎类同于BSD socket APIs。 UDT是多线程安全的…

全能终端神器MobaXterm

摘要&#xff1a;现今软件市场上有很多终端工具&#xff0c;比如&#xff1a;secureCRT、Putty等等。secureCRT其实也是一款很强大的终端工具&#xff0c;但它是收费软件&#xff0c;一般公司不允许使用。Putty&#xff0c;非常小巧&#xff0c;免费软件&#xff0c;但是不支持…

物业公司工作流应用方案

【基本需求】1、办公基本需求 企业要求办公自动化系统能适应不断变化的办公需求&#xff0c;提供解决公文处理等偏向个体的自动办公功能&#xff0c;还要求能解决单位之间、部门之间、管理人员之间信息交换与共享的需求。办公自动化系统首先要解决的就是提高信息的透明度&…

jquery ajax 异步分页,jquery 分页 Ajax异步

#th>th>nameth>操作th>tr>thead>tbody>ul>td>tr>tfoot>table>div>$(function() {init();showMenu();});//使用Ajax异步查询数据functionqueryPage(pageno) {vardataObj{"pageno": pageno,//pageno 是属性名称,是否增加双引号…