先看两个简单但诡异的代码:

0.1 + 0.2 > 0.3 // true

0.1 * 0.1 = 0.010000000000000002

0.1加0.2为什么就不等于0.3昵?要回答这个问题,得先了解计算机内部是如何表示数的。

计算机内部如何表示数

我们都知道,计算机用位来储存及处理数据。每一个二进制数(二进制串)都一一对应一个十进制数。

1. 计算机内部如何表示整数

这里以十进制数13来展示“按位计数法”如何表示整数:

十进制值

进制

按位格式

描述

13

10

13

1x10^1 + 3x10^0 = 10 + 3

13

2

1101

1x2^3 + 1x2^2 + 0x2^1 + 1x2^0 = 8 + 4 + 0 + 1

2. 计算机内部如何表示小数

再看小数怎么用按位计数法表示,以十进制数0.625为例:

十进制值

进制

按位格式

描述

0.625

10

0.625

6x10^-1 + 2x10^-2 + 5x10^-3 = 0.6 + 0.02 + 0.005

0.625

2

0.101

1x2^-1 + 0 x2^-2 + 1x2^-3 = 1/2 + 0 + 1/8

3. 如何用二进制表示0.1

关于十进制与二进制间如何转换,这里不细说,直接给出结论:

十进制整数转二进制方法:除2取余;十进制小数转二进制方法:乘2除整

十进制0.1转换成二进制,乘2取整过程:

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.1的二进制格式是:0.0001100011....。这是一个二进制无限循环小数,但计算机内存有限,我们不能用储存所有的小数位数。那么在精度与内存间如何取舍呢?

答案是:在某个精度点直接舍弃。当然,代价就是,0.1在计算机内部根本就不是精确的0.1,而是一个有舍入误差的0.1。当代码被编译或解释后,0.1已经被四舍五入成一个与之很接近的计算机内部数字,以至于计算还没开始,一个很小的舍入错误就已经产生了。这也就是 0.1 + 0.2 不等于0.3 的原因。

有误差的两个数,其计算的结果,当然就很可能与我们期望的不一样了。注意前面的这句话中的“很可能”这三个字?为啥是很可能昵?

0.1 + 0.1 为什么等于0.2

答案是:两个有舍入误差的值在求和时,相互抵消了,但这种“负负得正,相互抵消”不一定是可靠的,当这两个数字是用不同长度数位来表示的浮点数时,舍入误差可能不会相互抵消。

又如,对于 0.1 + 0.3 ,结果其实并不是0.4,但0.4是最接近真实结果的数,比其它任何浮点数都更接近。许多语言也就直接显示结果为0.4了,而不展示一个浮点数的真实结果了。

另外要注意,二进制能精确地表示位数有限且分母是2的倍数的小数,比如0.5,0.5在计算机内部就没有舍入误差。所以0.5 + 0.5 === 1

计算机这样胡乱舍入,能满足所有的计算需求吗

我们看两个现实的场景:

对于一个修建铁路的工程师而言,10米宽,还是10.0001米宽并没有什么不同。铁路工程师就不需要这么高0.x这样的精度

对于芯片设计师,0.0001米就会是一个巨大不同,他也永远不用处理超过0.1米距离

不同行业,要求的精度不是线性的,我们允许(对结果无关紧要的)误差存在。10.0001与10.001在铁路工程师看来都是合格的。

虽然允许误差存在,但程序员在使用浮点数进行计算或逻辑处理时,不注意,就可能出问题。记住,永远不要直接比较两个浮点的大小:

var a = 0.1

var b = 0.2

if (a + b === 0.3) {

// doSomething

}

JS中如何进入浮点数运算

将浮点运算转换成整数计算

整数是完全精度的,不存在舍入误差。例如,一些关于人民币的运算,都会以分为基本单位,计算采用分,展示再转换成元。当然,这样也有一些问题,会带来额外的工作量,如果那天人民币新增了一个货币单位,对系统的扩展性也会有考验。

bignumber.js会在一定精度内,让浮点数计算结果符合我们的期望。

{

let x = new BigNumber(0.1);

let y = new BigNumber(0.2)

let z = new BigNumber(0.3)

console.log(z.equals(x.add(y))) // 0.3 === 0.1 + 0.2, true

console.log(z.minus(x).equals(y)) // true

console.log(z.minus(y).equals(x)) // true

}

{

let x = 0.2

console.log(x * x === 0.04) // false

let y = new BigNumber(0.2)

let r = y.mul(y) // 0.04

console.log(r.equals(new BigNumber(0.04))) // true

}

更多例子,可以看bignumber.js官方示例。

小结

本文主要介绍了浮点数计算问题,简单回答了为什么以及怎么办两个问题:

为什么0.1 + 0.2 不等于0.3。因为计算机不能精确表示0.1, 0.2这样的浮点数,计算时使用的是带有舍入误差的数

并不是所有的浮点数在计算机内部都存在舍入误差,比如0.5就没有舍入误差

具有舍入误差的运算结可能会符合我们的期望,原因可能是“负负得正”

怎么办?1个办法是使用整型代替浮点数计算;2是不要直接比较两个浮点数,而应该使用bignumber.js这样的浮点数运算库

最后,本文只是简单回答了为什么,如果读者对更根本深入的原理感兴趣,可以自行google之。限于水平有限,本文如果有错误,欢迎指正。

python0.1+0.2不等于0.3_为什么0.1+0.2不等于0.3相关推荐

  1. android 相机拍照返回,Android6.0机型上调用系统相机拍照返回的resultCode值始终等于0的问题...

    版权声明:本文为博主原创文章,未经博主允许不得转载. 正常情况下调用系统相机拍照: 如果拍照后点击的是"确定"图标,返回的resultCode = -1(Activity.RESU ...

  2. 给出3个参数,N,M,K,怪兽有N滴血,等着英雄来砍自己,英雄每一次打击,都会让怪兽流失[0~M]的血量,到底流失多少?每一次在[0~M]上等概率的获取一个值,求K次打击之后,英雄把怪兽砍死的概率

    问题描述:给出3个参数,N,M,K,怪兽有N滴血,等着英雄来砍自己,英雄每一次打击,都会让怪兽流失[0~M]的血量,到底流失多少?每一次在[0~M]上等概率的获取一个值,求K次打击之后,英雄把怪兽砍死 ...

  3. 每天一道LeetCode-----给定一个矩阵,如果某个元素是0,就将所在行所在列上所有元素否置0

    Set Matrix Zeroes 原题链接Set Matrix Zeroes 给定一个m × n矩阵,如果矩阵中某个元素是0,那么就将它所在的行,所在的列上的所有元素都变成0.要求空间复杂度在O(1 ...

  4. 【C++代码】约瑟夫环问题:0,1,……,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。

    问题描述:0,1,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字.求出这个圆圈里剩下的最后一个数字. 这是力扣上的一道题.我的思路: ①首先想到的是用循环链表,每次向后遍历 ...

  5. 【MySQL 8.0 OCP 1Z0-908认证考试】题库精讲--第三讲mysql8.0安装配置升级(下)

    此专题题目较多,因此分为上中下三部分来讲,此为下篇. 完整版题库请到我的资源中下载,此为传送门.https://download.csdn.net/download/kanon_lgt/8501041 ...

  6. DXBBS V8.0 BBS论坛系统 简体中文正式版 [ 开发语言:ASP.NET 2.0 (C#) ]

    DXBBS V8.0 BBS论坛系统 简体中文正式版 [ 开发语言:ASP.NET 2.0 (C#) ] 程序版本:DXBBS V8.0 ACCESS/MSSQL版 开发语言:ASP.NET 2.0 ...

  7. vs 2022 下载了.net7.0预览版 还是提示当前.net sdk不支持将.net7.0设置为目标

    文章目录 官网下载.net 7.0预览版 运行项目提示错误 解决方法 官网下载.net 7.0预览版 链接:https://dotnet.microsoft.com/zh-cn/download/do ...

  8. usb3.0 ssd 测试软件,SSD打造的移动硬盘,顺便测下USB2.0、USB3.0、SATA2.0、SATA3.0传输速度...

    本帖最后由 神经病的春天 于 2014-9-18 15:36 编辑 近来升级了一下主机,这样一来,终于能够拥有USB3.0和SATA3.0接口了,不过本人目前还木有SSD,所以尚无法完全发挥出这些接口 ...

  9. 标题|服务器标题|服务器名称|服务器IP|服务器端口|是否自动展开(0不展开,1自动展开)|微端IP|微端端口(0表示不使用微端)|安全盾防火墙端口(0表示不使用防火墙)|防火墙类型,0=安全盾防火墙

    [Server] ; 标题|服务器标题|服务器名称|服务器IP|服务器端口|是否自动展开(0不展开,1自动展开)|微端IP|微端端口(0表示不使用微端)|安全盾防火墙端口(0表示不使用防火墙)|防火墙 ...

  10. 算法训练 - 反置数 一个整数的“反置数”指的是把该整数的每一位数字的顺序颠倒过来所得到的另一个整数。如果一个整数的末尾是以0结尾,那么在它的反置数当中,这些0就被省略掉了。比如说,124

    问题描述 一个整数的"反置数"指的是把该整数的每一位数字的顺序颠倒过来所得到的另一个整数.如果一个整数的末尾是以0结尾,那么在它的反置数当中,这些0就被省略掉了.比如说,1245的 ...

最新文章

  1. c++ 继承访问控制初步
  2. mark一下总是记混的重定向与转发的区别
  3. 09-CA/TA编程:storage demo
  4. 《Java多线程编程核心技术》读后感(十五)
  5. 2015 UESTC 搜索专题B题 邱老师降临小行星 记忆化搜索
  6. java游戏暂停弹出字体_小白写了个java的小游戏 想加个暂停的功能 无从下手 求大佬们帮...
  7. markdown使用积累1--$与$$
  8. 【CCF】 201604-1折点计数
  9. 强烈推荐《价值:我对投资的思考》
  10. 安装oracle11g未找到文件WFMLRSVCApp.ear文件
  11. [debug] PyCharm 退出 pytest in XXX.py,恢复run XXX.py
  12. Maven进行Mahout编程,使其兼容Hadoop2.2.0环境运行 (转)
  13. opencv图像分析与处理(6)- 二维取样定理与二维傅里叶变换
  14. DB2 表的常用命令
  15. FIS如何成为制霸北美的金融科技航母?丨亿欧解案例
  16. [RK3288][Android6.0] 调试笔记 --- RT5640播放时的Codec寄存器列表
  17. [十二省联考2019]希望
  18. Bitvise Tunnelier 安装教程及报错处理
  19. word流程图怎么使箭头对齐_word里流程图的直角箭头怎么画
  20. w ndows默认截图工具,windows截图工具快捷键

热门文章

  1. 安卓虚拟机 选择分辨率
  2. 各种快递API接口—爱快递
  3. 各平台安装OpenCV
  4. 已知 XYZ+YZZ=532,其中,X、Y、Z 为数字,编程求出 X、Y 和 Z 的值
  5. PAT 甲级 1015. Reversible Primes
  6. 常用电子个人邮箱账号格式怎么写?
  7. 前端工程师为什么会这么值钱?原来真相在这里
  8. js用函数找出1 - 1000 之间7的倍数和包含7的数字,放入数组内
  9. 想知道北京的公交线路图吗?用python给你画出来
  10. Word-VBA:删除选择区域的项目符号