js基石之Number:本质

js基石之数据类型一:类型分类&区别
js基石之数据类型二:类型判断
js基石之数据类型三:类型转换
js基石之Number:本质

本文可以回答的问题

  1. 为什么10 === 10.0
  2. 为什么0.1 + 0.2 !== 0.3
  3. 什么叫Number的最大安全值?
  4. 什么叫Number的最大值?
  5. js的数字精度问题具体是指什么?
  6. js的数字精度问题如何解决?
  7. 数字的不同进制是如何转换的?

Number真正的含义:只有浮点数问题1

在 JavaScript 中Number 类型,内部表示为双精度浮点型,即其他语言中的 double 类型,所以在 JavaScript 中实际上是没有整数类型的,数值都是按浮点数来处理的,存储方法相同。

所以看下面的例子:

虽然我们虽然写了一个整数10,在存储时和10.0是没有区别的 两者是等价的。

存储形式: 一切问题的根源

Number按照IEEE 754标准 存储为64位

IEEE 754标准的浮点数表示中公式为

- 1个符号位(sign)

0为正 1为负

- 11个指数位(exponent) : 决定这个数的大小范围,也就是它的量级[-1022,1023] 问题4 解决

在IEEE 754标准中,指数位使用了一种称为"偏移二进制"(bias representation)的表示方法,可以表示正指数和负指数。。

在没有偏移的情况下,指数部分有11位,那么它能表示的范围是0到2047(2的11次方减1)。

但是,由于使用了偏移量, IEEE 754规定,中间数作为偏移量,对于11位而言就是1023作为偏移量,可以把1023看做是0,小于1023的表示负指数,大于1023的才是正指数,所以实际的指数范围是-1023到1024。

需要注意的是,指数部分的最大值(全1)和最小值(全0)在IEEE 754标准中被保留用于表示特殊值,如无穷大、无穷小和非数(NaN)。所以,实际可用的指数范围是-1022到1023。

- 52个尾数位(mantissa) : 决定这个数的精度,也就是它的具体值。问题3 解决

在IEEE 754标准的浮点数表示中,尾数部分的位数是52位。然而,这个尾数部分实际上表示的是一个小数,小数点后面的位数。在二进制中,这个小数的形式是1.xxxx,其中xxxx是尾数部分的52位。 这里的1.xxxx实际上是一个隐含的1,也就是说,我们实际上有53位的精度,而不仅仅是52位。这就是为什么尾数部分的最大值是2的53次方减1,而不是2的52次方减1。 这种表示方法被称为"规格化",它的目的是为了最大化浮点数的精度。通过隐含一个额外的1,我们可以得到更高的精度,同时也可以表示更大的数。

例子 问题2 解决

整数转换为二进制 除2取余直到 结果为0

ini 复制代码
let a = 12;
12/2 = 6 余 0 
6/2 = 3  余 0 
3/2 = 1  余 1
1/2 = 0  余 1
所以二进制表示为 1100

计算机按照IEEE 754标准存储:将二进制1100转换为标准表示法 1.100 * 2^3

符号位 正数 0

尾数位 为100 不满52位后面补0

指数位 因为小数点向左移动3位 所以指数为3 按照标准需要加上偏移量1023 所以指数部分为1026 转换为二进制10000000011 如果不满11位前面补0

所以最后的结果是

0 10000000010 1000000000000000000000000000000000000000000000000000

小数转换为二进制 乘2取整 直到小数部分为0

ini 复制代码
let a = 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
....无限循环了
所以二进制为:0011 0011 0011 ...无限循环

计算机按照IEEE 754标准存储:将二进制 0011 0011 0011...无限循环 转换为标准表示法 1.1001100110011... * 2^3

符号位 正数 0

尾数位 100110011001100110011...直至填满52位

指数位 因为小数点向右移动3位 所以指数为-3 按照标准需要加上偏移量1023 所以为1020 转换为二进制01111111100 如果不满11位前面补0.

所以最后的结果是

0 01111111100 1001100110011001100110011001100110011001100110011001 
第53位是1 已经达到最大位数了 后面依然有数且是1 需要四舍五入 
所以最终结果
0 01111111100 1001100110011001100110011001100110011001100110011010

精度问题: 问题5 解决

大数运算:

JavaScript能够准确表示的最大整数是2的53次方减1。超过这个范围的整数,在JavaScript中无法准确表示。例如,如果你试图将2的53次方加1,结果仍然是2的53次方。

ini 复制代码
2**53 + 1 === 2**53 // true

小数的精度:

由于JavaScript使用的是64位浮点数表示,所以小数的精度是有限的。如果一个小数的精度超过了这个范围,那么它在JavaScript中的表示就会有误差。

ini 复制代码
0.1 + 0.2 === 0.3 // false
因为在转换为二进制的时候四舍五入有误差 所以最后的结果肯定也是不准确的。

特殊值的处理:

在JavaScript中,有一些特殊的值,如Infinity(无穷大)、-Infinity(无穷小)和NaN(非数)。这些值在进行运算时,可能会有一些非常规的行为,这也是JavaScript的数值精度问题的一部分。

总结下:

关于数值精度问题 并不是js独有的问题 只要是符合IEEE 754标准的都会有这个问题。例如java同样也是不准确的。

csharp 复制代码
public class Main {
    public static void main(String[] args) {
        double a = 0.1;
        double b = 0.2;
        double c = 0.3;

        if (a + b == c) {
            System.out.println("0.1 + 0.2 equals 0.3");
        } else {
            System.out.println("0.1 + 0.2 does not equal 0.3");
        }
        System.out.println(a + b);
    }
}

// 结果
0.1 + 0.2 does not equal 0.3
0.30000000000000004

精度问题解决方案 问题6 解决

大数运算解决 BigInt

javascript 复制代码
const bigNumber = BigInt(`${2**53 + 1 }`);
const bigNumber2 = BigInt(`${2**53 + 2 }`);
console.log(bigNumber + bigNumber2) // 18014398509481986n

浮点数运算解决

  • 转换为整数计算

  • 使用第三方库:

    有许多第三方库可以帮助你进行精确的浮点数运算。例如,decimal.jsbignumber.jsmathjs等。这些库提供了一套完整的数学运算函数,可以用来进行精确的浮点数运算。

  • 使用toFixed()或toPrecision()方法:

    这两个方法可以将一个浮点数四舍五入到指定的小数位数或有效数字。这可以用来解决一些简单的精度问题。但是,需要注意的是,这两个方法返回的是一个字符串,而不是一个数字。

  • 使用ES6中的Number.EPSILON:

    Number.EPSILON是JavaScript中表示可接受的最小误差的常数。在比较两个浮点数是否相等时,可以使用Number.EPSILON来判断它们的差的绝对值是否小于Number.EPSILON。

javascript 复制代码
function numbersCloseEnoughToEqual(n1, n2) { 
    return Math.abs(n1 - n2) < Number.EPSILON; 
} 
console.log(numbersCloseEnoughToEqual(0.1 + 0.2, 0.3)); // true

进制相互转换问题7 解决

十进制转N进制: toString

scss 复制代码
(15).toString(2)   // '1111'
(15.1).toString(2)
//'1111.0001100110011001100110011001100110011001100110011'
(15).toString(8)   // '17'
(15).toString(16)  // 'f'

N进制转十进制: parseInt

javascript 复制代码
Number.parseInt('11',2)  // 3
Number.parseInt('11',16) // 17
Number.parseInt('11',8)  // 9
  1. mdn_Number
  2. sec-ecmascript-language-types-number-type
  3. javascript-number
  4. ruanyifeng_number
相关推荐
浮华似水18 分钟前
简洁之道 - React Hook Form
前端
正小安2 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
小飞猪Jay4 小时前
C++面试速通宝典——13
jvm·c++·面试
_.Switch4 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光4 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   4 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   4 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web4 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常4 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇5 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器