js toFixed()四舍五入丢失精度问题处理
错误展示
看了下lodash的代码,大概是通过使用科学计数法扩大10的n次,将操作数化为整数运算,可以避免精度丢失。
/**
* Creates a function like `_.round`.
*
* @private
* @param {string} methodName The name of the `Math` method to use when rounding.
* @returns {Function} Returns the new round function.
*/
function createRound(methodName) {
var func = Math[methodName];
return function(number, precision) {
number = toNumber(number);
precision = toInteger(precision);
if (precision) {
// Shift with exponential notation to avoid floating-point issues.
// See [MDN](https://mdn.io/round#Examples) for more details.
var pair = (toString(number) + 'e').split('e'),
value = func(pair[0] + 'e' + (+pair[1] + precision));
pair = (toString(value) + 'e').split('e');
return +(pair[0] + 'e' + (+pair[1] - precision));
}
return func(number);
};
}
var round = createRound('round');
执行结果:
另外一种方法-来自文心一言,稍作改动
/**
* 四舍五入
* @param num
* @param precision 保留位数
* @param isReturnNumber 默认返回数字类型,否则返回字符串类型
* @returns {Number}
*/
export function numberToFixed(num: number, precision = 2, isReturnNumber = true) {
if (isNaN(num)) return '';
if (precision < 0 || precision > 20) {
throw new RangeError('precision must be between 0 and 20');
}
const factor = Math.pow(10, precision);
const value = Math.round(num * factor) / factor;
// 转换为字符串并填充零(如果需要)
const stringValue = value.toString();
const dotIndex = stringValue.indexOf('.');
let integerPart = '';
let decimalPart = '';
if (dotIndex < 0) {
// 没有小数部分
integerPart = stringValue;
decimalPart = '';
} else {
integerPart = stringValue.substring(0, dotIndex);
decimalPart = stringValue.substring(dotIndex + 1);
}
if (decimalPart.length < precision) {
// 小数部分不足,需要补零
const result = integerPart + '.' + decimalPart.padEnd(precision, '0');
return isReturnNumber ? Number(result) : result;
}
// 小数部分足够,直接返回
return isReturnNumber ? Number(stringValue) : stringValue;
}