问题描述
js中的浮点数有精度问题:
- 浮点数精度问题,比如
0.1 + 0.2 !== 0.3
解决方案
demo地址
把浮点数转化为字符串,模拟实际运算的过程。
import React, { useState } from 'react';
import { InputNumber } from 'antd';
import styles from './FloatNumSum.less';
export interface FloatNumSumProps extends React.HTMLAttributes<HTMLDivElement> {}
const FloatNumSum = (props: FloatNumSumProps) => {
const { className = '', ...otherProps } = props;
const [floatNumList, setFloatNumList] = useState([1.1, 1.2, 1.3]);
const sumFloatNum = (x: number, y: number) => {
const stringX = `${x}`;
const stringY = `${y}`;
const integerX = stringX.includes('.') ? stringX.split('.')[0] : x;
const decimalX = stringX.includes('.') ? stringX.split('.')[1] : 0;
const integerY = stringY.includes('.') ? stringY.split('.')[0] : y;
const decimalY = stringY.includes('.') ? stringY.split('.')[1] : 0;
return Number(`${Number(integerX) + Number(integerY)}.${Number(decimalX) + Number(decimalY)}`);
};
const sum = () => {
return floatNumList.reduce((x, y) => {
return sumFloatNum(x, y);
});
};
const sumJs = () => {
return floatNumList.reduce((x, y) => x + y);
};
function onChange(value: number, index: number) {
const newFloatNumList = [...floatNumList];
newFloatNumList[index] = value;
setFloatNumList(newFloatNumList);
}
return (
<div className={`${styles.root} ${className}`} {...otherProps}>
<InputNumber min={0} value={floatNumList[0]} onChange={(e) => onChange(e, 0)} />
<InputNumber min={0} value={floatNumList[1]} onChange={(e) => onChange(e, 1)} />
<InputNumber min={0} value={floatNumList[2]} onChange={(e) => onChange(e, 2)} />
<h3></h3>
<h3>javascriot的计算结果:</h3>
<div>{`${floatNumList[0]}+${floatNumList[1]}+${floatNumList[2]}=${sumJs()}`}</div>
<h3>优化后的计算结果:</h3>
<div>{`${floatNumList[0]}+${floatNumList[1]}+${floatNumList[2]}=${sum()}`}</div>
</div>
);
};
export default FloatNumSum;
其他方案
如何解决浮点数运算的精度问题,有 3 种思路:
- 考虑到每次浮点数运算的偏差非常小(其实不然),可以对结果进行指定精度的四舍五入,比如可以
parseFloat(result.toFixed(12)) ; - 将浮点数转为整数运算,再对结果做除法。比如0.1 + 0.2,可以转化为
(1*2)/3 。 - 把浮点数转化为字符串,模拟实际运算的过程。
先来看第一种方案,在大多数情况下,它可以得到正确结果,但是对一些极端情况,toFixed 到 12 是不够的,比如:
210000 * 10000 * 1000 * 8.2
parseFloat(17219999999999.998.toFixed(12));
上面的情况,如果想让结果正确,需要 toFixed(2) ,这显然是不可接受的。
再看第二种方案,比如 number-precision 这个库就是使用的这种方案,但是这也是有问题的,比如:
123456.789 * 123456.789
所以,最终考虑使用第三种方案,目前已经有了很多较为成熟的库,比如 bignumber.js,decimal.js,以及big.js等。我们可以根据自己的需求来选择对应的工具。并且,这些库不仅解决了浮点数的运算精度问题,还支持了大数运算,并且修复了原生toFixed结果不准确的问题。
参考资料
JavaScript 中精度问题以及解决方案
|