車間的工人在生產出來產品後,須要完成初步的自檢,並經過手機上報。在實際生產中,用戶(工人)不方便進行數值的輸入,於是表單中的一些項設計成 picker 模式以供選取數值。數值的取值範圍,根據容許的偏差範圍生成。示例以下:javascript
示例一 // 偏差 0.01mm ~ 0.06mm // picker 展現的數值 0.01, 0.02, 0.03, 0.04, 0.05, 0.06 示例二 // 偏差 15mm ~ 18mm // picker 展現的數值 15, 16, 17, 18 示例三 // 偏差 1.05mm ~ 1.1mm // picker 展現的數值 1.05, 1.06, 1.07, 1.08, 1.09, 1.1
由以上例子能夠得知,取值範圍的計算是根據偏差範圍的最小值的最小位數做爲基數,從最小值(包含)逐步累加至最大值(包含)。java
首先,根據最小值獲取小數位的個數。數組
function getDecimalPlace(value) { // 先將 Number 轉換爲 String value = value + ''; // 查找小數點的位置,加 1 是爲了方便計算小數點的位數。 var floatIndex = value.indexOf('.') + 1; // 返回的結果是小數位的個數 return floatIndex ? value.length - floatIndex : 0; }
用幾個實際的數值,測試一下這個方法。測試
getDecimalPlace(1); //0 getDecimalPlace('1.0'); //0 getDecimalPlace('1.5'); //1 getDecimalPlace('1.23'); //2
而後,根據小數位的個數計算累加的基數。優化
var min = 0.01; var max = 0.06; var decimal = getDecimalPlace(min); // 基數 var radixValue = Math.pow(10, -decimal);
最後,根據偏差範圍和基數,循環生成取值範圍。prototype
var value = min; var range = []; for (; value <= max; value += radixValue) { range.push(value); } console.log(range); //結果:[0.01,0.02,0.03,0.04,0.05]
從結果來看,好像哪裏不對。沒錯,最大值 0.06 沒有出如今取值範圍中。設計
JavaScript 採用了 IEEE-754 浮點數表示法。這是一種二進制表示法,二進制浮點數表示法並不能精確表示相似 0.1 這樣簡單的數字。
經過一個簡單的例子來驗證上面這段話。code
var num1 = 0.2 - 0.1; var num2 = 0.3 - 0.2; console.log(num1 === num2); //false console.log(num1 === 0.1); //true console.log(num2 === 0.1); //false
由此可知,前面計算取值範圍的方法中,遇到了相似的問題。ip
var max = 0.06; var value = 0.05; console.log(value + 0.01 === max); //false
由於從 0.05 + 0.01
的結果並不等於 0.06
,因此循環只執行了 5 次(而非預期的 6 次)就結束了。ci
在嘗試修復此問題以前,先把前面的代碼封裝一下。
function getRange(min, max) { var decimal = getDecimalPlace(min); var radixValue = Math.pow(10, -decimal); var value = min; var range = []; for (; value <= max; value += radixValue) { range.push(value); } return range; }
最簡單粗暴的辦法,就是調整循環條件,在循環結束後再將最大值添加至數組。
function getRange(min, max) { var decimal = getDecimalPlace(min); var radixValue = Math.pow(10, -decimal); var value = min; var range = []; for (; value < max; value += radixValue) { range.push(value); } range.push(max); return range; }
再次使用以前的數據測試:
getRange(0.01, 0.06); //結果:[0.01,0.02,0.03,0.04,0.05,0.06]
運行結果與預期一致,問題解決。
然而,後續的測試中又出現了意外。
getRange(1.55, 1.65); // 結果:[1.55,1.56,1.57,1.58,1.59,1.6,1.61,1.62,1.6300000000000001,1.6400000000000001,1.65]
1.6300000000000001
這樣的數值,顯然不是咱們指望獲得的。出現此現象,與以前的問題緣由一致。
方案一
將參與計算的數值,先轉換爲整型,再進行計算。
function getRange(min, max) { var decimal = getDecimalPlace(min); var radixValue = Math.pow(10, -decimal); var multi = Math.pow(10, decimal) var value = min * multi; var range = []; for (; value < max * multi; value += radixValue * multi) { range.push(value / multi); } range.push(max); return range; }
注意事項:
方案二
使用 toFixed()
方法,對浮點型進行格式化。
function getRange(min, max) { var decimal = getDecimalPlace(min); var radixValue = Math.pow(10, -decimal); var value = min; var range = []; for (; value < max || +value.toFixed(decimal) === max; value += radixValue) { range.push(+value.toFixed(decimal)); } return range; }
注意事項:
JavaScript 中浮點型精度的偏差,是很是基礎可是卻又常常不被重視的問題。文中分享的方案,足以覆蓋項目中的全部狀況,但若是用在其它地方或項目中,在一些極端狀況下可能會有問題。