js實現小數點四舍五入
其實這個問題,在之前的面試中被提問到了,由於筆者平時都是用原生的
toFixed()
的方法來保留小數點,所以當時並沒有回答出來這個問題,嗚嗚嗚. 😹
現在突然想起了這個問題,就研究一下吧。
最簡單的實現方法
可以使用
Math
對象的一些方法來實現,這個比較簡單,主要用到了Math.round
和一些簡單的乘除法運算。例子
思路:
1. 先把數值轉成只有一位小數點
的數值
2. 利用Math.round
方法四舍五入(關鍵)
3. 最后通過乘除法運算等到想要的小數點位數
function toFixed(num,decimal){
if(isNaN(num)){
return 0;
}
num = num-0;
var p1 = Math.pow(10, decimal + 1);
var p2 = Math.pow(10, decimal);
console.log(num * p1 / 10);
console.log(Math.round(num * p1 / 10));
return (Math.round(num * p1 / 10) / p2).toFixed(decimal); //思考一下,為什么要除10?
}
運行一下上面的代碼,其實有隱藏的bug... 💔
toFixed(2.555,2) //2.56
toFixed(4100.065,2) //4100.06 ???
console.log(4100065/10) //410006.49999999994 這就是bug的答案
0.1+0.2=? //0.30000000000000004
所謂的隱藏bug,就是js編程語言的小數點精度問題,所以上面那個除於10 只是在一定的范圍內有效,過了這個范圍,還是會出現精度問題...
用字符串處理
既然小數點進行運算會出現問題,那我們換一種思路,用字符串來處理。例子 😄
思路:
1. 把數字轉成字符串,然后把小數點移動到倒數第二位。(模擬只有一位小數點)
2. 還是用到Math.round
來四舍五入
3. 重復第一個步驟,不過把小數點移動到(你要保留多少位小數點)
function toFixed(num,decimal){
if(isNaN(num)){
return 0;
}
var strnum = num+'';
var arr = strnum.split('.');
if(arr.length<2){
return num.toFixed(decimal);
}
strnum = arr.join('');
var strnum2 = strnum.slice(0,-1)+'.'+strnum.slice(-1);
var result = Math.round(strnum2-0)+'';
if(arr[1].length==decimal){
result+='0'; //如果小數點的個數剛好等於要保留的小數點個數,要補0
}
return result.slice(0,-decimal)+'.'+result.slice(-decimal)
}
2020-04 更新
上面的字符串處理不夠好,下面補充個新的邏輯方式:
- 把數字轉成字符,並記錄
.
小數點的位置;如果沒有小數點,則直接在后面補0; - 通過小數點的位置,計算出原數字有多少位小數(oldPointNum);把小數點去掉並且把字符串轉成數組;
- 通過比較oldPointNum和n(要保留的位數);如果oldPointNum<n,直接補0;否則下一步
- 比較數組倒數(i =oldPointNum-n)的數字是否>=5;是則進一位;i++;循環該步驟(核心),用這一步來模擬四舍五入。
const toFixed = (number, n) => {
let numberStr = number + "";
let reg = /^(-|\+)?(\d+(\.\d*)?|\.\d+)$/i;
if(!reg.test(numberStr)) {
console.error('輸入的數字格式不對');
return;
}
let sign = numberStr.charAt(0) === '-' ? (numberStr=numberStr.slice(1),-1):1; // 判斷是否是負數
let pointIndex = numberStr.indexOf("."); // 記錄小數點的位置
if (pointIndex > -1) {
numberStr = numberStr.replace(".", "");
} else { // 沒有小數點直接添加補0;
numberStr += ".";
numberStr+=new Array(n).join('0');
return numberStr;
}
let numberArray = numberStr.split(""); //轉成數組
let len = numberArray.length;
let oldPointNum = len - pointIndex; // 獲取原數據有多少位小數
if (oldPointNum < n) { // 要保留的小數點比原來的要大,直接補0
while (n - oldPointNum > 0) {
numberArray.push(0);
n--;
}
} else if (oldPointNum > n) { // 模擬四舍五入
let i = oldPointNum - n; // 從倒數第i個數字開始比較
let more = numberArray[len - i] >= 5 ? true : false;
while (more) {
i++;
more = +numberArray[len - i] + 1 === 10 ? true : false; // 進位后判斷是否等於10,是則繼續進位
numberArray[len - i] = more&&i!==(len+1) ? 0 : +numberArray[len - i] + 1; // 其他位置的數字進位直接變成0,第一位的例外
console.log(i, len);
}
numberArray.length = len- (oldPointNum-n); // 截取多余的小數
}
numberArray.splice(pointIndex, 0, ".");
return sign===-1?'-'+numberArray.join(""):numberArray.join("");
};
ps :上面的代碼有點臃腫,其實可以通過正則來簡化,以后有空修改。