js为什么会存在数字精度丢失的问题,以及如何进行解决
举例:0.1 + 0.2 === 0.3 //false
计算机存储双精度浮点数需要首先把十进制转换成二进制的科学计数法形式
计算规则:符号位(1)+指数位(11)+指数位偏移量的二进制+小数部分(52) 共64位
在该规则下,会存在十进制小数无法完全准确表示为二进制小数,在转换过程中会造成计算误差,导致精度问题。
解决方案: 1、针对需要展示的数据
function strip(num: number, precision = 16) {
//precision最大值为16
const val = +parseFloat(num.toPrecision(precision))
console.info(val)
return val
}
strip(333.333)
strip(5555.111111123456789)
2、先转换成整数操作后再转换成浮点数
/** * 精确加法 */
function add(num1, num2) {
const num1Digits = (num1.toString().split('.')[1] || '').length
const num2Digits = (num2.toString().split('.')[1] || '').length
const baseNum = Math.pow(10, Math.max(num1Digits, num2Digits))
return (num1 * baseNum + num2 * baseNum) / baseNum
}
3、第三方库 Math.js 或者BigDeciml.js
解释下 JavaScript 中的 Number.EPSILON
Number.EPSILON 静态数据表示1 与大于 1 的最小浮点数之间的差值
2-52,或大约 2.220446049250313e-16(chrome 浏览器打印的值)。
应用 1、相等测试
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false
function equal(x, y) {
return Math.abs(x - y) < Number.EPSILON;
}
const x = 0.2;
const y = 0.3;
const z = 0.1;
console.log(equal(x + z, y)); // true
实际上 EPSILON 指定了数字“1”的精确度
然而,对于任何具有更大数量级的算术运算,Number.EPSILON 是不适用的。如果你的数据数量级在 10^3 的范围,那么小数部分的精确度将远远小于Number.EPSILON:
function equal(x, y) {
return Math.abs(x - y) < Number.EPSILON;
}
const x = 1000.1;
const y = 1000.2;
const z = 2000.3;
console.log(x + y); // 2000.3000000000002;误差为 10^-13 而不是 10^-16
console.log(equal(x + y, z)); // false
此时,需要更大的容差, 2000 * Number.EPSILON 的乘积可以为此情况提供足够的容差
function equal(x, y, tolerance = Number.EPSILON) {
return Math.abs(x - y) < tolerance;
}
const x = 1000.1;
const y = 1000.2;
const z = 2000.3;
console.log(equal(x + y, z, 2000 * Number.EPSILON)); // true
如何在 JavaScript 中判断一个数字是整数?
isFinite 只有类型为数字且为有限数的值才返回 true,而非数字的值始终返回 false。
isInteger 整数时返回true,浮点数返回false
请注意,由于ECMAScript 浮点数编码(IEEE-754)的精度限制。例如,5.0000000000000001 只与 5 相差 1e-16,这个差值太小了而无法表示。因此,5.0000000000000001 将使用与 5 相同的编码表示,从而使得 Number.isInteger(5.0000000000000001) 返回 true。
const { isInteger } = Number //是否为整数 整数返回true
export function isNumber(v: unknown): v is number {
return typeof v === 'number' && !Number.isNaN(v) && Number.isFinite(v)
}
export function isInt(v: unknown): boolean {
return isNumber(v) && isInteger(v)
}
export function isFloat(v: unknown): boolean {
return isNumber(v) && !isInteger(v)
}
解释下 NaN(Not a Number)在浮点数运算中的作用。
表示数学上未定义或不可表示的结果
- 0/0
- Infinity/Infinity 、Infinity - Infinity
- 非数学运算,比如对负数开平方根、对负数取对数等
- 使用未初始化的变量进行运算
- 当阶码(指数部分)全为1时,只要位数不全为0 就是NaN
# js超过Number最大值该如何处理(超大数 运算失去精度)
使用 BigInt
const bigInt1 = BigInt(9007199254740991)
const bigInt2 = BigInt(2)
const result1 = bigInt1 + bigInt2 // 9007199254740993n
console.log(result1) // 输出: 9007199254740993n
使用库
npm install bignumber.js
const BigNumber = require('bignumber.js');
const num1 = new BigNumber('9007199254740991');
const num2 = new BigNumber('2');
const result = num1.plus(num2);
console.log(result.toString()); // 输出: 9007199254740993
字符串处理
/**
*
* @param {*} str1 字符串
* @param {*} str2 字符串
* @returns
*/
function addBigNumber(str1, str2) {
const length1 = str1.length
const length2 = str2.length
const length = Math.max(length1, length2)
const arr1 = str1
.padStart(length, 0)
.split('')
.map(item => Number(item))
const arr2 = str2
.padStart(length, 0)
.split('')
.map(item => Number(item))
let res = ''
let flag = 0
console.info(str1, str2)
for (let i = length - 1; i >= 0; i--) {
let result = arr1[i] + arr2[i] + flag
flag = Math.floor(result / 10)
res = `${result % 10}` + res
}
if (flag) {
res = `${flag}` + res
}
return res
}
const str1 = '91112'
const str2 = '33231'
const result = addBigNumber(str1, str2)
console.log('结果', result)