JS中数值类型的本质
一、JS中的数值类型
众所JS爱好友周知,JS中只有一个总的数值类型——number,它包含了整型、浮点型等数值类型。其中,浮点数的实现思想有点复杂,它把一个数拆成两部分来存储。第一部分是有效位数,也可以称作系数、分数或者尾数;第二部分被称为指数,表示小数点位应该插在系数的哪个位置。在JS中,浮点数由IEEE 754标准非完全实现。一个number包含1个符号位,11位指数为和53位有效位数。IEEE 754基于二进制运作,分为两个部分组成。第一部分包含两个子部分:符号位和有效位数。符号位在最高位中,该位为1表示该数是负数,为0则表示正数;有效位数在64位的最低几位中,通常表示一个范围内的小数。
0.5 <= 有效位数 < 1.0
按照这种表示法,有效位数的最高位理论上始终为1。因此,该位实际上并不需要被存放于number 当中,于是就多出了能用的1位,称作彩蛋位。
第二部分指数存在于符号位和有效位数之间那些位中。存放号后,可以用如下公式表示一个数值:
数值 = 符号位 * 系数 * (2 ** 指数)
下面来分析一下数值类型的本质。
二、JS中数值类型的本质
在了解JS中数值类型的本质之前,我们先来看一个问题:0.1 + 0.2 = ?。
我相信很多人都见过JS中这个经典的计算。是的,0.1 + 0.2 != 0.3,而是等于0.30000000000000004。这是为什么呢,下面我们就来剖析一下这种现象出现的本质原因。看一下代码:
function deconstruct(number) {
let sign = 1;
let coefficient = number;
let exponent = 0;
//将符号位从系数中提取出来
if (coefficient < 0) {
coefficient = -coefficient;
sign = -1;
}
if (Number.isFinite(number) && number !== 0) {
//-1128 就是 Number.MIN_VALUE的指数减去有效位数再减去彩蛋位的结果
exponent = -1128;
let reduction = coefficient;
//将系数不断除以 2,直到趋近于 0 为止
while (reduction !== 0) {
//将除的次数与-1128相加到exponent
exponent += 1;
reduction /= 2;
}
//当指数为 0 的时候,可以认为数值是一个整数
//如果指数不为0,则通过校正系数来使其为 0
reduction = exponent;
while (reduction > 0) {
coefficient /= 2;
reduction -= 1;
}
while (reduction < 0) {
coefficient *= 2;
reduction += 1;
}
}
return {
sign,
coefficient,
exponent,
number
};
}
现在,我们看看传入1的结果:

根据数值公式计算:1 * 9007199254740992 * (2 ** -53) = 1 。
这显然是没有问题的。
那么我们将0.1传入函数呢,会产生怎样的结果,接着来看。

根据数值公式计算:
1 * 7205759403792794 * 2 ** -56 = 0.1000000000000000055511151231257827021181583404541015625。
结果出人意料!JS无法精确的处理小数。这是由IEEE 754机制决定的。
因此,0.1 + 0.2 != 0.3很好解释了。
那么,我们怎么在业务中,使得0.1 + 0.2 = 0.3呢?下面给出解决办法:

Number.toFixed()用于保留小数点具体位数进行四舍五入,参数默认为0,则不保留小数。Number.toPrecision()用于保留并四舍五入到指定的数字位数,默认全部保留,参数为0,则保留1位有效数字。注意,以上两个函数均返回string类型的值,所以在这里需要将其显示转换为number类型。
JS中数值类型的本质的更多相关文章
- js中数值类型相加变成拼接字符串的问题
如题,弱类型计算需要先进行转型,例: savNum=parseInt(savNum)+parseInt(num);或者使用 number()转型
- JS中Float类型加减乘除 修复 JQ 操作 radio、checkbox 、select LINQ to SQL:Where、Select/Distinct LINQ to SQL Count/Sum/Min/Max/Avg Join
JS中Float类型加减乘除 修复 MXS&Vincene ─╄OvЁ &0000027─╄OvЁ MXS&Vincene MXS&Vincene ─╄Ov ...
- JS中基本类型与包装类型的关系
对于JS中一些类型的转化的东西,自己测试并得出的结论,有错误的地方请大大们留言. 不多废话,直接贴代码,测试请直接拷贝全部代码: <!DOCTYPE html> <html> ...
- 推断js中的类型:typeof / instanceof / constructor / prototype
怎样推断js中的类型呢,先举几个样例: var a = "jason"; var b = 123; var c = true; var d = [1,2,3]; var e = n ...
- js中boolean类型的理解
<html> <head> <script type="text/javascript"> var x="12"; aler ...
- js中属性类型:数据属性与访问器属性
js中属性类型分为两种:数据属性和访问器属性 在js中,对象都是由名值对构成的,名:就是我们所说的属性名,值就是属性对应的值(基本值.对象.方法). ECMA-262第5版定义了只有内部才用的特性,描 ...
- JS中的类型识别
JS为弱类型语言,所以类型识别对JS而言尤为重要,JS中常用的类型识别方法有4种:typeof.Object.prototype.toString.constructor和instanceof. (1 ...
- MSSQL中 数值类型转换为千分号的解决方案
转自:http://www.maomao365.com/?p=4797 前言:最近需要将报表中关于数值部分的数据,采用千分号的形式展现给用户,下面将讲解如何制作1 将数值类型转换为 money类型2 ...
- js中date类型的格式转化为yyyy-MM-dd HH:mm:ss的String类型
在vue中或其他框架中可以在Date的原型链中添加Format的方法,如ruoyi可以写在main.js中更好,如果写在utils还需要去导入包. 正常的js直接放到utils.js就好 Date.p ...
随机推荐
- Luogu3398 仓鼠找sugar (LCA)
第一发lg[]没开够RE了,下了数据本地一直停止运行,还以为是dfs死了,绝望一交,A了... 判断\(x\)是否在路径\(s-t\)上,只需满足 \(dep_{x} >= dep_{LCA(s ...
- Hadoop的由来、Block切分、进程详解
Hadoop的由来.Block切分.进程详解 一.hadoop的由来 Google发布了三篇论文: GFS(Google File System) MapReduce(数据计算方法) BigTable ...
- [NOIP2018提高组] 保卫王国 (树链剖分+动态DP)
题面 题目链接-Luogu 题目链接-Loj(要加Freopen) 题解 什么是动态DP? OneInDark:你不需要知道这么多,你只需要知道是利用了广义矩阵乘法就够了! 广义矩乘 广义矩阵乘法,简 ...
- DES|3DES|AES|RSA|DH | CA | SSL(HTTPS)
1.对称密钥算法: 加解密速度块,算法使安全的,已知算法无法推出密钥.但是密钥的分发困难. DES:对称密钥算法,是一种块加密算法,只有一个密钥.加解密都是用一个密钥. 3DES:与DES一样,可以认 ...
- Java代码自动计算机
答案(这里面包括main方法不要写重了),copy即可使用: public static void main(String[] args) { while (true){ System.out.pri ...
- awk5个使用场景
awk简介 首先要知道awk的使用场景,需了解awk有哪些优势与短板. 关于个人近期学习awk总结其优势: awk对文本的处理运算效率同比其他工具效率高很多(比shell的for循环高10倍以上,运算 ...
- 解决nuxt/koa架构初始项目运行报错问题
今天在学习运用vue的nuxt/koa框架,初始化项目之后,在执行 $> npm run dev 时报错,错误详细信息如下: 点击查看报错的详细内容 > npm run dev Debug ...
- 异步编程promise
异步编程发展 异步编程经历了 callback.promise.async/await.generator四个阶段,其中promise和async/await使用最为频繁,而generator因为语法 ...
- python的三层架构
项目目录规范 Foo/ |-- core/ # 存放业务逻辑相关代码 | |-- core.py | |-- api/ # 存放接口文件,接口主要用于为业务逻辑提供数据操作. | |-- api.py ...
- 在Portainer上管理其他docker主机(这只是其中一种方式),另一种方式看这个文档:使用Portainer管理其他主机的docker应用有两种方式
其他主机开启远程连接docker端口 需要设置一下2375端口的监听.通过修改docker配置文件方式进行监听. 修改配置文件修改监听端口 使用Centos7安装的docker,所以下面的配置是适用于 ...