前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >0.3-0.2 = 0.09999999999999998 问题解析(精度缺失问题)

0.3-0.2 = 0.09999999999999998 问题解析(精度缺失问题)

作者头像
何处锦绣不灰堆
发布2024-01-04 08:19:52
1280
发布2024-01-04 08:19:52
举报
文章被收录于专栏:农历七月廿一农历七月廿一
写在前面

这个问题其实一直存在,我也看了很多博主写的文章,但是没有一篇文章真的说明白了这个问题,所以今天我尽量将这个问题讲明白,废话不多说,开整

问题表象

研究一下0.3 - 0.2 不等于0.1的问题,做前端时间久的人都避不开精度缺失的问题,今天我们就研究透他,关于0.3 - 0.2 = 0.09999999999999998 这个问题

其实这个问题不是javascript独有的,很多语言都有这个问题,下面是我用不同语言打印出来的0.3 - 0.2 的问题

  • Java
代码语言:javascript
复制
public class HelloWorld {
    public static void main(String []args) {
       System.out.println(0.3- 0.2);
	   // 0.09999999999999998
    }
}
  • Python
代码语言:javascript
复制
#!/usr/bin/python
# Write Python 3 code in this online editor and run it.
print(0.3 - 0.2);
# 0.09999999999999998
  • Swift
代码语言:javascript
复制
/* Write swift code in this online editor and run it. */
var myString = 0.3-0.2
print(myString)
/* 0.09999999999999998*/
  • TS
代码语言:javascript
复制
const hello : number = 0.3 - 0.2
console.log(hello)
// 0.09999999999999998
  • Rust
代码语言:javascript
复制
fn main() {
    println!("{}",0.3-0.2);  0.09999999999999998
}
问题分析

要解释这个问题其实也不复杂,就是解释一下计算机和不同的语言之间是怎么交流的,我们要明白一件事就是不管语言本身有多高级,表象都是按照语言本身的语法规则进行开发我们认为计算机可以看得懂的逻辑,但是其实本质是这门语言按照他之前定好的规则进行进制的转换,最终转为计算机看得懂的二进制,也就是说计算机本身只认识0和1,这就是为什么说计算机是由0和1组成的,知道了这个本质上面的事情就好解释了,下面我们拿javascript进行展示,

当我们输入0.3给计算机的时候,他会转成二进制,转换结果为:

代码语言:javascript
复制
0.3.toString(2) // 0.010011001100110011001100110011001100110011001100110011

这个时候我们发现他是一个无限循环小数,我们计算机数据交互式先存储,再读取,既然是存储,那么就意味着存储的空间是有限的,那么一个无限循环的小数是不可能一直被存储的,所以计算机只能做一个切断的处理,具体切断多少位呢?这里可以参考IEEE754(国际规定的舍入规则,说人话就是avaScript 中进行浮点数运算时,只有前 15 到 17 位是精确的,超出这个范围的数字可能会出现精度损失), 那么切断之后我们保留小数位数多一点,将他的精度扩大之后可以发现(我们保留17位)

代码语言:javascript
复制
0.2.toPrecision(17) //0.20000000000000001
0.3.toPrecision(17) //0.29999999999999999

看到上面的两个数据,我们所谓的0.3 - 0.2 在计算机里面是 0.29999999999999999 - 0.20000000000000001 = 0.09999999999999998

这就是为什么这个结果值是0.09999999999999998的原因。

为什么不是所有的小数都这样呢?其实这个也很好解释,只要转为二进制之后不是无限循环或者无限不循环的小数都不会有问题,比如0.5 转为二进制之后就是0.1 ,所以他的计算不会出问题

解决办法

js 可以使用第三方库进行处理,比如decimaljs或者BigNumberjs,当然不想引入的话,也可以直接将原始数据放大之后缩小即可,其实本质就是转为整数进行处理,因为整数没有这个问题,比如

代码语言:javascript
复制
(0.3 * 1000 - 0.2 * 1000)/1000 // 0.1

他的运行我们也可以验证

代码语言:javascript
复制
(0.3 * 1000).toPrecision(17) // 300.00000000000000
(0.2 * 1000).toPrecision(17) // 200.00000000000000
(300.00000000000000 - 200.00000000000000) / 1000 // 0.1
问题影响

这个问题其实是一个比较严重的问题,特别是银行行业,我们平常写业务代码的时候不注意这个没关系,因为最后最多就是几分钱的差异,但是任何小事都经不过放大,银行的金额一般都是数量比较大的,所以当一个小的差异就很可能被无限放大,最后就会差别很离谱,所以这个还是需要注意的,上面说了decimaljs是可以处理的,那么看了他的readme文件,大概可以推测出来他其实就是将我们的小数转为了string类型的字符串进行处理,这样可以最大程度保持原始值。所以这个问题还是需要注意的。

decimal.js用法
代码语言:javascript
复制
// 引入CDN https://cdnjs.cloudflare.com/ajax/libs/decimal.js/10.4.3/decimal.min.js
let a =  new Decimal(0.3) 
let b = new Decimal(0.2) 
console.log(a.minus(b).toString()) // 0.1 // 包括加法(plus)、减法(minus)、乘法(times)、除法(div)、乘方(pow)、开方(sqrt)
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-01-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在前面
  • 问题表象
  • 问题分析
  • 解决办法
  • 问题影响
  • decimal.js用法
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档