0.1+0.2!==0.3,为什么?

地精撕裂者
• 阅读 1451

先说结论

为什么不等于?

因为浮点数表示小数的时候有精准度损失

为什么会有精准度损失

因为计算机硬件存储数据时,是以二进制(10101010)形式进行

比如说每个字节是 8 位,int 类型占 4 个字节,也就是 32 位精度;那么 32 位的计算机精度可以存 2 的 32次方个数据。如下图:

0.1+0.2!==0.3,为什么?

每位上面可以放两个二进制数据也就是 0 或者 1;一般最高位上是符号位(1表示负数,0表示正数),所以带符号的类型数据应该是 31 个 2

2 2 2 ... 2(31个2),加上符号范围就是 -2147483648 ~ 2147483647;当然也有无符号整形,暂不讨论

那么小数怎么存呢?小数在计算机当中叫浮点型,JS 最终会由浏览器引擎转成 C++,但是 JS 当中只有一种数值类型,那就是 number,那么 number 在 C++ 是什么类型呢;

我们暂且认为它是双精度类型,也就是 double,C++ 中占四个字节,也就是 64 位存储,整数存储参考上面即可,重点说说浮点存储

同样 64 位可分为三部分,它的制定格式是以 IEEE 754 为标准:

第一部分:符号位(S),占 1 位即第 63 位;

第二部分:指数域(E),占 11 位即 52 位到 62 位,含 52 和 62;

第三部分:尾数域(F),占 52 位即第 0 位到 51 位,含 51;

0.1+0.2!==0.3,为什么?

如果将一个小数转换成二进制 64 位怎么表示,以 12.52571 为例

  • 先转换成二进制(十进制转换成二进制)(站长工具二进制转换

    • 12.52571 => 1100.100001101001010011101110001110010010111000011111
  • 将其小数点向左偏移三位

    • 1.100100001101001010011101110001110010010111000011111 * 2^3

得出结论

  1. 因为是整数,所以符号位 S 是 0;
  2. 因为向左偏移了三位,所以 E = 1023 + 3 = 1026(转化为二进制) => 10000000010,有 11 位,不够前面补 0

    • 为什么要加1023?为什么左移是加3,不是减3
  3. 尾数是(F)(小数点后面)100100001101001010011101110001110010010111000011111;

最终表示: 0 10000000010 100100001101001010011101110001110010010111000011111;

上面总长度是63位,差一位,最后面补零,即

0 10000000010 1001000011010010100111011100011100100101110000111110;

那么12.52571的64位计算机存储形式就是上面了;

回过头看 0.1 + 0.2

上面的表达可能有些疑惑,肯定的,毕竟笔者也是参考的(权当笔记,供以后温习),暂且不表;那么 0.1 和 0.2 是怎么转的

这里就有一个问题,0.1 和 0.2 转成二进制小数点后面是循环的

// 0.1 转化为二进制
0.0 0011 0011 0011 0011...(0011无限循环)
// 0.2 转化为二进制
0.0011 0011 0011 0011 0011...(0011无限循环)

由于尾数只有52位(52位之后的被计算机截掉了)

E = -4; F =1001100110011001100110011001100110011001100110011010 (52位)
E = -3; F =1.1001100110011001100110011001100110011001100110011010 (52位)

要让两个数相加,首先E需要相同,于是得出下面

E = -3; F =0.1100110011001100110011001100110011001100110011001101 (52位) //多余位截掉
E = -3; F =1.1001100110011001100110011001100110011001100110011010 (52位)

上面两个相加得出

E = -3; F = 10.0110011001100110011001100110011001100110011001100111
-------------------------------------------------------------------
E = -2; F = 1.00110011001100110011001100110011001100110011001100111

得出的结论就是

2^-2 * 1.00110011001100110011001100110011001100110011001100111

这个值转换成真值,结果为: 0.30000000000000004

如何做到精准度

JavaScript 的类型 bigInt (ES8)中

TypeScript 也有这样的类型

有解决精准度问题的 big.js、bigInt 库

同样有精准度缺失的语言

python

总结

因为 JavaScript 到最后会转换为 C++ 去执行

在 IEEE754 标准中常见的浮点数数值表示有:单精准度(32位)和双精准度(64位),JS 采用的是后者。浮点数与整数不同,一个浮点数既包含整数部分,又包含小数部分,因为其表示法的不同,需要分析为整数和小数部分,然后相加得到结果。0.1 和 0.2 先转成二进制,在转换为同一维度计算,得到二进制后,再转换为十进制后,就成了0.30000000000000004

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
添砖java的啾 添砖java的啾
4年前
distinct效率更高还是group by效率更高?
目录00结论01distinct的使用02groupby的使用03distinct和groupby原理04推荐groupby的原因00结论先说大致的结论(完整结论在文末):在语义相同,有索引的情况下groupby和distinct都能使用索引,效率相同。在语义相同,无索引的情况下:distinct效率高于groupby。原因是di
Easter79 Easter79
3年前
typeScript数据类型
//布尔类型letisDone:booleanfalse;//数字类型所有数字都是浮点数numberletdecLiteral:number6;lethexLiteral:number0xf00d;letbinaryLiteral:number0b101
皕杰报表(关于日期时间时分秒显示不出来)
在使用皕杰报表设计器时,数据据里面是日期型,但当你web预览时候,发现有日期时间类型的数据时分秒显示不出来,只有年月日能显示出来,时分秒显示为0:00:00。1.可以使用tochar解决,数据集用selecttochar(flowdate,"yyyyMMddHH:mm:ss")fromtablename2.也可以把数据库日期类型date改成timestamp
Stella981 Stella981
3年前
Python Challenge Level 18
初学Python,挑战一下流行的PythonChallenge,很不幸,卡在了18关~~被字符字节码之间的转换搞得焦头烂额,不过终于搞定了还是很happy的~~~主要的问题就是16进制形式的字符如何转成字节码(注意:不是encoding)如:\'89','50','4e','47','0d','0a','1a','0a','00
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
MySQL的一些小问题
在建表的时候,总有一些模糊的点让我不得不百度:1、金钱字段存储应当选择float、decimal、double?应当选择decimal。他是以字符串形式存储的,不会损失精度,为啥浮点型的(单精度、双精度)会损失精度呢,看下面的例子:定点数如果不写精度和标度,则按照默认值decimal(10,0)来操作,也就是存储11.1时候,会自
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
遥感数字图像处理
遥感数字图像处理的特点:同遥感图像的光学处理(即模拟图像处理)相比,遥感数字图像的计算机处理有很多优点,主要表现在以下几点:1.图像信息损失低,处理的精度高由于数字遥感图像是用二进制表示的,在图像处理时,其数据存储在计算机数据库中,不会因长期存储而损失信息,也不会因处理而损失原有信息。对计算机来说,不管是对4bit还是对8
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(