一、前言
与其他语言不同,在JS中不区分整数与浮点数,统一为浮点数。所有数字类型统一都是基于 IEEE 754 标准来实现;采用的是“双精度”格式(即 64 位二进制)。这也意味着在小数计算时,会发生精度误差的情况(这并不是js独有的,其他基于IEEE 754标准的都会有这样的问题)。
二、属性方法
2.1 定义数字类型
JS中的数字常量一般用十进制表示。例如:
const a = 42;
const b = 42.3;
数字前后的 0 可以省略:(不建议,请规范写法)
const a = 0.42;
const b = .42;
const c = 42.0;
const d = 42.;
数字常量还可以用其他格式来表示,八进制和十六进制;如果前缀为 0,则 JavaScript 会把数值常量解释为八进制数,如果前缀为 0 和 “x”,则解释为十六进制数。例如:
const a = 070;
const b = 0xFF;
2.2 特殊的数值
2.2.1 最大值:Number.MAX_SAFE_INTEGER
最大整数是 2^53 - 1,即 9007199254740991,在 ES6 中被定义为Number.MAX_SAFE_INTEGER。
2.2.2 最小值:Number.MIN_SAFE_INTEGER
最小整数是 -9007199254740991,在 ES6 中被定义为 Number.MIN_SAFE_INTEGER。
2.2.3 无穷数
在进行运算时,可能都遇到过编译错误或者运行时错误,例如“除以 0”。这个时候不会得到无穷数,正无穷Infinity(Number.POSITIVE_INFINITY)与负无穷-Infinity(Number.NEGATIVE_INFINITY)。
2.2.4 不是数字的数字:NaN
如果数学运算的操作数不是数字类型(或者无法解析为常规的十进制或十六进制数字),就无法返回一个有效的数字,这种情况下返回值为 NaN。可以通过Number.isNaN(值)判断。
const a = 2 / "测试"; // NaN
a == NaN; // false
a === NaN; // false
Number.isNaN(a); // true
注意:在运算的时候要注意避免得到NaN,防止程序出现错误;
2.2.5 零值
JavaScript 有一个常规的 0(也叫作+0)和一个 -0。-0 除了可以用作常量以外,也可以是某些数学运算的返回值。
const a = 0 / -2; // -0
const b = 0 * -2; // -0
// 但是规范定义的返回结果是这样
a.toString(); // "0"
a + ""; // "0"
String( a ); // "0"
// 但是-0字符串翻过来就是对的
+"-0"; // -0
Number( "-0" ); // -0
JSON.parse( "-0" ); // -0
// 0与-0比较都为true
-0 == 0; // true
-0 === 0; // true
注意:需要注意正负0的比较,使用-0场景例如动画帧的移动速度等。
2.3 数字的方法
- toSting:以字符串返回数值
- toFixed:返回字符串值,它包含了指定位数小数的数字(会四舍五入)
- toPrecision: 返回字符串值,它包含了指定长度的数字
const a = 22;
a.toString(); // '22'
a.toFixed(3); // '22.000'
const b = 2.222;
b.toPrecision(2); // '2.2'
b.toPrecision(3); // '2.22'
2.4 变量转换为数字
- Number:将变量转换为数字
- parseInt:返回数字的整数部分
- parseFloat:返回一个浮点数
Number('22.2'); // 22.2
parseInt('22.2'); // 22
parseFloat('22.2'); // 22.2
Number('测试'); // NaN
parseInt('测试'); // NaN
parseFloat('测试'); // NaN
注意:上述三个方法转换时,如果转换失败会返回NaN,开发中要注意避免。
三、实战示例
3.1 浮点数计算出现精度缺失
3.1.1 发现问题
在日常项目中会遇到小数之间的算术运算,例如商品价格的计算、账户金额的统计;涉及的小数时有时候会出现精度误差计算结果不准确,导致客户反馈bug。
示例:
console.log(0.1 + 0.2); // 0.30000000000000004
3.1.2 问题原因
我们在上面有介绍过在JS中所有数字都是以IEEE-754标准格式64位浮点数形式储存,1与1.0是相同的。因为有些小数以二进制表示位数是无穷的。JavaScript会把超出53位之后的二进制舍弃。所以并不是JS中才会出现,所有基于IEEE-754标准的浮点数计算都会有这样的问题。
3.1.3 解决方案
使用第三方类库
如果在项目中多处都需要使用浮点数计算,这个时候就需要考虑引用第三方类库方法来进行小数点之间的计算。这里推荐第三方类库:
- Math.js
- decimal.js
- big.js
简易封装
如果只是单纯处理个别计算问题时,可以使用下面简易封装的简易加法运算。核心思路就是:计算小数点后最大有几位,转换成10的指数幂进行计算,最后还原。
/**
* 精度相加
*/
const addTogether = (n1, n2) => {
let res1 = n1.toString().split(".")[1]
? n1.toString().split(".")[1].length
: n1.toString().split(".")[0].length;
let res2 = n2.toString().split(".")[1]
? n2.toString().split(".")[1].length
: n2.toString().split(".")[0].length;
let pow = Math.max(res1, res2);
let base = Math.pow(10, pow);
return (n1 * base + n2 * base) / base;
};
3.2 计算出现NaN
3.2.1 发现问题
在项目中设计到表单输入时,有时候会进行两个变量之间的计算,有时候展示结果会出现NaN的情况。
3.2.2 问题原因
在进行算术运算时,会将变量类型隐式转换为数字类型;如果转换原类型不是字符串的数字时无法转换为数字,就会返回NaN
。
3.2.3 解决方案
涉及到表单问题时要设置输入框为number类型,在计算之前需要提前转换为Number类型,如果转换为NAN要及时报错或者根据业务需求兼容处理。
const num = "测试";
const curNum = Number.isNaN(Number(num)) ? 0 : Number(num);