【编程课堂】震惊!小 bug 引发大灾难,0.1 + 0.2 的结果竟然是……

简介: 手边有电脑的同学可以立即在 python 控制台下尝试一下,对浮点数精度不够了解的同学可能会大呼:天啦噜,夭寿啦,怎么会是 False !

各位观众点进标题看文章的时候,我已经准备打包行李去UC报道啦~



冷笑话结束,嗯,说正事。


请大家思考一下在 python 控制台输入  0.1 + 0.2 == 0.3 ,返回的结果是什么?


手边有电脑的同学可以立即在 python 控制台下尝试一下,对浮点数精度不够了解的同学可能会大呼:天啦噜,夭寿啦,怎么会是 False


没错 ,不管是在 Python,还是 C++、Java、JavaScript 等其他语言中,都是 False


为什么会出现这样的结果?首先我们要了解,在计算机的存储类型为二进制,十进制的 0.1 与 0.2 在计算机中会已二进制的形式表示,规则如下:


十进制小数转换成二进制小数采用”乘2取整,顺序排列”法。具体做法是:用2乘十进制小数,可以得到积,将积的整数部分取出,再用2乘余下的小数 部分,又得到一个积,再将积的整数部分取出,如此进行,直到积中的小数部分为零,或者达到所要求的精度为止。


以 0.1 为例,我们做一下转换:


步数 算式 结果
1 0.1 * 2 = 0.2 取 0
2 0.2 * 2 = 0.4 取 0
3 0.4 * 2 = 0.8 取 0
4 0.8 * 2 = 1.6 取 1
5 0.6 * 2 = 1.2 取 1
6 0.2 * 2 = 0.4 取 0
7 0.4 * 2 = 0.8 取 0
8 0.8 * 2 = 1.6 取 1
……

比较第二步和第六步,可以得知, 已二进制 表示 0.1 最终的结果为一个无限循环的数


0.0001100110011…… ,但由于计算机的存储位数是有限的,并不能存储一个无限循环的数。对于 Python 来说,浮点数有 53 位精度。为了把这个数存起来,必然会丢失部分精度,造成误差,所以最终的近似结果为:


0.00011001100110011001100110011001100110011001100110011010


同理, 对 0.2 的处理也是一样。所以当两个存在误差的数相加,其结果也必定会出现误差,这就解释了在计算机中为什么 0.1 + 0.2 不等于 0.3。


浮点数精度的知识远不止此,摊开来讲一本书也讲不完,所以对于初学者来说只要知道有这么回事就行了,之后再遇到就不要惊讶了,日常工作中可遵循以下准则:


  1. 尽量避免使用小数比较大小,比较两个小数是否相等时可写成 abs(a - b) < 0.000001。即两个数的差值足够小。
  2. 确保数组的索引都是整数。
  3. 按分(而不是元)计算金额。百分比放大100倍计算以避免出现小数。
  4. Python3 使用除法 / 时需注意,它的结果总是小数,整除的符号是 //
  5. 避免在同一个表达式中使用相差太大或太小的数值。将很小的数值和很大数值相加,小的数值很可能被当作 0。


当然,在某些科研、财务等对精度要求比较高的领域中,Python 提供 decimal 模块准确控制精度。

它具有以下特点:


  1. 提供十进制数据类型,并且存储为十进制数序列;
  2. 有界精度:用于存储数字的位数是固定的,可以通过 decimal.getcontext().prec=x来设定,不同的数字可以有不同的精度


可以通过整数、字符串或者元组构建 decimal.Decimal,对于浮点数需要先将其转换为字符串


>>>from decimal import *
# 设置精度为 7 位
>>>getcontext().prec = 7
>>>Decimal(1) / Decimal(7)
Decimal('0.1428571')


参考文章


Python官方对于浮点数的解释:https://docs.python.org/2/tutorial/floatingpoint.html

decimal 模块:https://docs.python.org/2/library/decimal.html
代码之谜:http://justjavac.iteye.com/blog/1724438

Android计算器低级错误?都是二进制惹的祸!:http://www.guokr.com/article/27173/


近期文章推荐阅读:

给伸手党的福利:Python 新手引导

只学2个月编程能写出什么代码?他们表示:You can you code!

如何用100行Python代码做出魔性声控游戏“八分音符酱”

数据分析:当赵雷唱民谣时他唱些什么?

一行代码扫出“敬业福”

我扒了杜蕾斯的微博

Python 爬虫爬取美剧网站

今天,你抢到票了吗?

爆款游戏《贪吃蛇大作战》的 Python 实现

相关文章
|
C语言
加速github 下载速度的方法
加速github 下载速度的方法
3246 1
|
网络安全
mac系统charles无法抓包如何解决
mac系统charles无法抓包如何解决
1356 0
|
4月前
|
人工智能 自然语言处理 测试技术
Apipost智能搜索:只需用业务语言描述需求,就能精准定位目标接口,API 搜索的下一代形态!
在大型项目中,API 数量庞大、命名不一,导致“找接口”耗时费力。传统工具依赖关键词搜索,难以应对语义模糊或命名不规范的场景。Apipost AI 智能搜索功能,支持自然语言查询,如“和用户登录有关的接口”,系统可理解语义并精准匹配目标接口。无论是新人上手、模糊查找还是批量定位,都能大幅提升检索效率,降低协作成本。从关键词到语义理解,智能搜索让开发者少花时间找接口,多专注核心开发,真正实现高效协作。
|
7月前
|
数据库 数据安全/隐私保护 UED
如何开发CRM系统中的订单管理板块(附架构图+流程图+代码参考)
本文详细介绍了CRM系统中订单管理板块的开发与实现,涵盖功能设计、业务流程、开发技巧及代码参考。通过构建高效订单管理系统,企业可提升销售效率与客户满意度,并优化整体运营流程。
|
算法 C# 开发工具
《黑神话:悟空》背后的编程语言揭秘——超越C++的多元技术融合
【8月更文挑战第27天】在游戏开发领域,一款游戏的成功往往离不开其背后强大的技术支持和编程语言的精妙运用。《黑神话:悟空》作为备受瞩目的国产单机动作游戏,其开发过程不仅涉及了多种编程语言,更是一次技术创新的集中展现。然而,当我们深入探讨其开发语言时,会发现它并非仅依赖于单一的C++,而是融合了多种编程语言的优势,共同铸就了这款游戏的辉煌。
717 0
|
IDE 测试技术 开发工具
NumPy 代码调试与错误处理
【8月更文第30天】NumPy 是 Python 中用于科学计算的核心库之一,提供了高性能的多维数组对象和大量的数学函数。尽管 NumPy 提供了许多方便的功能,但在实际编程过程中难免会遇到各种各样的问题。本文将介绍一些调试 NumPy 代码的技巧,并讨论如何处理常见的错误。
917 2
|
存储 程序员 编译器
【C/C++ 堆栈以及虚拟内存分段 】C/C++内存分布/管理:代码区、数据区、堆区、栈区和常量区的探索
【C/C++ 堆栈以及虚拟内存分段 】C/C++内存分布/管理:代码区、数据区、堆区、栈区和常量区的探索
1232 0
|
SQL 存储 Cloud Native
CDN转存离线日志到OSS,实现更长时间的日志存储
CDN转存离线日志到OSS,实现更长时间的日志存储
808 1
CDN转存离线日志到OSS,实现更长时间的日志存储
|
机器人 BI 数据处理
伙伴客户案例|阿里云RPA&电讯盈科企业方案助力宁夏煤业降本增效——财务场景篇
RPA全称机器人流程自动化(Robotic Process Automation),是一种新兴的“数字劳动力”,可以替代或辅助人完成规则明确的重复性劳动,大幅提升业务流程效率,实现企业业务流程的自动化和智能化,从而降本增效。目前,RPA解决方案的应用场景几乎涵盖了所有行业,包括银行、保险、制造、零售、医疗、物流、电子商务甚至政府和公共机构。
伙伴客户案例|阿里云RPA&电讯盈科企业方案助力宁夏煤业降本增效——财务场景篇
|
机器学习/深度学习 编解码 算法
目标检测算法之FPN(附FPN代码实现)
目标检测算法之FPN(附FPN代码实现)
目标检测算法之FPN(附FPN代码实现)