思路是将小数分割成整数和小数部分然后进行四舍五入再补全字符串后的0
目前没发现有什么问题。有bug请反馈谢谢
代码原创,转载请注明连接谢谢
"use strict";
/**
* https://my.oschina.net/isgaoyi/blog/4717508 - v1.0 (2020-11-15T01:46:32+0800)
* Copyright 2020 [email protected]
*
* 修复四舍五入精度问题
* @method Number.toFixed()
* @param {int} param 需要保留的精度
* @param {int} rejection=4 四舍五入的4,改成3就变成3舍4入
* @return {string} 返回值
*/
Number.prototype.toFixed = function (Length, Rejection = 4) {
const length = ~~Length; //取整
const rejection = ~~Rejection; //取整
if (length < 0 || length > 100) {
throw new Error(
"RangeError: toFixed() digits argument[0] must be between 0 and 100"
);
}
if (rejection < 0 || rejection > 10) {
throw new Error(
"RangeError: toFixed() digits argument[1] must be between 0 and 10"
);
}
let tStr = this.toString(),
index = tStr.indexOf("."),
arr = [tStr.slice(0, index)],
res;
if (index !== -1) {
arr[1] = tStr.slice(index + 1, tStr.length);
}
if (this > Number.MAX_SAFE_INTEGER) {
return Number(arr[0]).toExponential();
}
if (arr.length <= 1) {
res = this.toString();
} else {
if (arr[1][length] > rejection) {
// 截断到需要入的后一位
arr[1] = arr[1].slice(0, length + 1);
if (length === 0) {
arr[0] = (Number(arr[0]) + 1).toString();
} else {
// 获取小数位前面有多少个0
let zero = /^0+/.exec(arr[1]);
zero = zero ? zero[0] : "";
// 截断后加一
arr[1] = arr[1].slice(0, length);
// 记录原始长度
const before = Number(arr[1]).toString();
const later = (Number(arr[1]) + 1).toString();
// 如果发生整体进位
if (later.length > before.length || Number(arr[1]) === 0) {
if (zero.length === 0) {
// 整数位加1,小数位去掉最高位
arr[0] = Number(arr[0]) + 1;
arr[1] = later.slice(1, arr[1].length + 1);
} else {
// 将0字符的长度减小1
zero = zero.slice(0, zero.length - 1);
arr[1] = later;
}
} else {
// 没有发生整体进位,直接计算小数位
arr[1] = later;
}
arr[1] = zero + arr[1];
}
} else {
// 直接截断
arr[1] = arr[1].slice(0, length);
}
res = length === 0 ? arr[0] : arr[0] + "." + arr[1];
}
// 补全多余的0
if (arr[1] && arr[1].length < length) {
for (let i = 0; i < length - arr[1].length; i++) res += "0";
} else if (!arr[1] && length !== 0) {
res += ".";
for (let i = 0; i < length; i++) res += "0";
}
return res;
};
要注意的是js的Number,整数位越长,小数位精度就越短。
性能分析
console.time("timer");
for(var i=0;i<10000000;i++){
(2.5376).toFixed(3)
}
console.timeEnd("timer");
// 2.5376
// 原生:timer: 2244.384033203125 ms
// 2.5376)修改后:timer: 1666.916015625 ms
// 2.0099 (较慢的情况,需要考虑小数前的0,和进位的情况)
// 原生:timer: 2393.131103515625 ms
// 修改后:timer: 2914.43310546875 ms
// 2.1111 (较快的情况,只需要直接截取)
// 原生:timer: 2343.195068359375 ms
// 修改后:timer: 883.989013671875 ms
优化后综合来看性能比原生还强。以下是性能优化的总结
toString()略快于Sting()
~~ Number,优于parseInt(),但是转换非数字可能会报错,由于这里最大100,最小0,所以可以放心食用。
indexOf加slice,分割字符串性能优于split
一般情况的可靠性测试
测试前我们先将上面的方法名改为【toFixed2】避免覆盖原生方法,然后执行下面的代码
let a = 0.0001
console.time("timer");
for(var i=0;i<10000000;i++){
a = i*0.0001;
let x = (2.5376+a).toFixed(3),y = (2.5376+a).toFixed2(3)
if (!(x === y || x-y < 0.001 || y-x <0.001)){
console.log(((2.5376+a)+':'),(2.5376+a).toFixed(3),(2.5376+a).toFixed2(3))
}
}
console.timeEnd("timer");
当程序运行完成后并没有弹出一条控制台信息,即认为该方法适用于一般情况的四舍五入