亮神知识库 亮神知识库
首页
  • 手写代码

    • 手写代码系列
  • 基础知识

    • 基础
    • JS底层
    • CSS
  • 原理
  • 浏览器
  • HTTP
  • 网络安全
  • babel
  • webpack基础
  • webpack进阶
  • Vite
  • TypeScript
  • Vue2
  • Vue3
  • Node基础

    • glob
    • 模块化机制
    • 事件循环
    • KOA2框架原理
    • Node子进程
    • cluster原理(了解)
  • 教育行业2021

    • B端
    • C端
    • 工具
  • 游戏行业2025
  • 刷题
  • 杂(待整理)
  • 学习
  • 面试
  • 实用技巧
  • 心情杂货
  • 年度总结
  • 友情链接
关于
  • 分类
  • 标签
  • 归档
  • 收藏
GitHub (opens new window)

亮神

前程明亮,未来可期
首页
  • 手写代码

    • 手写代码系列
  • 基础知识

    • 基础
    • JS底层
    • CSS
  • 原理
  • 浏览器
  • HTTP
  • 网络安全
  • babel
  • webpack基础
  • webpack进阶
  • Vite
  • TypeScript
  • Vue2
  • Vue3
  • Node基础

    • glob
    • 模块化机制
    • 事件循环
    • KOA2框架原理
    • Node子进程
    • cluster原理(了解)
  • 教育行业2021

    • B端
    • C端
    • 工具
  • 游戏行业2025
  • 刷题
  • 杂(待整理)
  • 学习
  • 面试
  • 实用技巧
  • 心情杂货
  • 年度总结
  • 友情链接
关于
  • 分类
  • 标签
  • 归档
  • 收藏
GitHub (opens new window)
  • 基础

    • 贪婪匹配
    • 数字类型
      • 面试
    • rem原理
    • 移动端适配
  • 手写代码

  • JS底层深入

  • CSS

  • 基础
  • 基础
0zcl
2021-07-20
目录

数字类型

# 数字类型

ECMAScript 中的 Number 类型使用 IEEE754 标准来表示整数和浮点数值。所谓 IEEE754 标准,全称 IEEE 二进制浮点数算术标准,这个标准定义了表示浮点数的格式等内容。

在 IEEE754 中,规定了四种表示浮点数值的方式:单精确度(32位)、双精确度(64位)、延伸单精确度、与延伸双精确度。像 ECMAScript 采用的就是双精确度,也就是说,会用 <code>64 位字节来储存一个浮点数</code>

# 十进制 转 二进制

在JS内部所有的计算都是以二进制方式计算的。 所以运算 0.1+0.2 时要先把 0.1 和 0.2 从十进制转成二进制。

0.1转化成二进制的算法:

  • 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.4*2=0.8======取出整数部分0
  • 0.8*2=1.6======取出整数部分1
  • 0.6*2=1.2======取出整数部分1

所以0.1转化成二进制是:0.0001 1001 1001 1001......

0.2转化成二进制的算法:

  • 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.4*2=0.8======取出整数部分0
  • 0.8*2=1.6======取出整数部分1
  • 0.6*2=1.2======取出整数部分1

所以0.2转化成二进制是:0.0011 0011 0011 0011......

这里要注意 0.1 和 0. 2转成的二进制是无穷的。另外在现代浏览器中是用浮点数形式的二进制来存储二进制,所以还要把上面所转化的<code>二进制转成浮点数形式的二进制</code>

# 二进制转成浮点数形式的二进制

双精度浮点数用1位表示符号位,11位表示指数位,52位表示小数位,如下图所示 浮点数

  • 符号位:正数为0,负数为1
  • 指数位:阶数+偏移量。阶数是:2^(e-1)^-1,e为阶码的位数;偏移量是把小数点移动到整数位只有11时移动的位数。正数表示向左移,负数表示向右移
  • 小数位:即二进制小数点后面的数

接下来把 0.1 转成的二进制 0.0001 1001 1001 1001...... 转成浮点数形式的二进制。

先要把小数点移动到整数位只有1,要向右移动4位,故偏移量为−4,通过指位数的计算公式2^(11-1)^-1-4 = 1019,把 1019 转成二进制为1111111011,不够11位要补零,最终得出指位数为01111111011;

小数位为100110011001......,因为小数位只能保留52位,第53位为1故进1。 转换结果如下图所示: 浮点数

# 浮点数相加

先把阶码调整为相同

0.1 是 1.1001100110011…… * 2^-4,阶码是 -4,而 0.2 就是 1.10011001100110...* 2^-3,阶码是 -3,两个阶码不同,所以先调整为相同的阶码再进行计算,调整原则是小阶对大阶,也就是 0.1 的 -4 调整为 -3,对应变成 0.11001100110011…… * 2^-3

接下来是尾数计算:

  0.1100110011001100110011001100110011001100110011001101
+ 1.1001100110011001100110011001100110011001100110011010
————————————————————————————————————————————————————————
 10.0110011001100110011001100110011001100110011001100111
1
2
3
4

我们得到结果为 10.0110011001100110011001100110011001100110011001100111 * 2^-3

将这个结果处理一下,即结果规格化,变成 1.0011001100110011001100110011001100110011001100110011(1) * 2^-2

括号里的 1 意思是说计算后这个 1 超出了范围,所以要被舍弃了。

再然后是舍入,四舍五入对应到二进制中,就是 0 舍 1 入,因为我们要把括号里的 1 丢了,所以这里会进一,结果变成

1.0011001100110011001100110011001100110011001100110100 * 2^-2

本来还有一个溢出判断,因为这里不涉及,就不讲了。

所以最终的结果存成 64 位就是

0 01111111101 0011001100110011001100110011001100110011001100110100

将它转换为10进制数就得到 0.30000000000000004440892098500626

因为<code>两次存储时的精度丢失加上一次运算时的精度丢失</code>,最终导致了 0.1 + 0.2 !== 0.3

参数:https://github.com/mqyqingfeng/Blog/issues/155

# 面试

问:0.1+0.2 等于 0.3吗? 为什么不等于

答:不等于。因为 JS 是用64来保存 浮点数。1位存符号位,11位存指数位,剩下52位存小数位。而0.1 和 0.2 的二进制是 无限循环的。所以0.1、0.2转成浮点数的二进制时,会对52位后的二进制进行舍去(53位为1,进1;为0舍弃)。这是第一次精度丢失。0.1 和 0.2 浮点数二进制相加导致 溢出一个位。要进行舍弃。这是第二次精度丢失。由于以上两种<code>精度丢失</code>,所以0.1+0.2不等于0.3

问:“0.1+0.2不等于0.3会引起那些BUG?

答:

  • 引起统计页面展示错乱的BUG,展示多个小数点
  • 还有300.01优惠300元后,支付金额不足0.01元等类似的BUG

问:怎么解决0.1+0.2不等于0.3这个问题? 请封装一个方式来解决精度问题

答: <code>Number.EPSILON</code>的实质是一个可以接受的最小误差范围. 如果误差能够小于Number.EPSILON,我们就可以认为结果是可靠的

function equal(left, right) {
  return Math.abs(left - right) < Number.EPSILON
}
// equal(0.1-0.2, 0.1)
// false
// equal(0.1-0.2, -0.1)
// true
1
2
3
4
5
6
7
编辑 (opens new window)
上次更新: 2025/07/20, 06:21:22
贪婪匹配
rem原理

← 贪婪匹配 rem原理→

最近更新
01
2024年
07-20
02
2023年
07-20
03
2022年
07-20
更多文章>
Theme by Vdoing | Copyright © 2025-2025 亮神 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式