19年目標:消滅英語!我新開了一個公衆號記錄一個程序員學英語的歷程javascript
有提高英語訴求的小夥伴能夠關注公衆號:csenglish 程序員學英語,天天花10分鐘交做業,跟我一塊兒學英語吧html
近期在項目中有出現大數值的訂單號
9148368244236619
在調用接口時自動變成9148368244236620
的狀況,致使請求失誤。本文特地總結了出現這種狀況的緣由,以及js精度相關的狀況。前端
在本次案例中,訂單號是後端同步渲染到頁面上的,java
<div class="j_OrderNumber" data-ordernumber="9148368244236619"></div>
複製代碼
呈如今頁面上的訂單號數值沒有問題9148368244236619
,前端此時想獲取到這個訂單號:jquery
var orderNumber = $('.j_OrderNumber).data('ordernumber'); // 9148368244236620 typeof(orderNumber) === 'number' // true 複製代碼
在這個取值賦值的過程當中,超過安全值的大數值發生了變化,從9148368244236619
變成了91483682442366120
,這裏的問題主要出如今jquery
的data
方法。程序員
超過安全值Math.pow(2, 53)-1 -- 9007199254740991
的number類型在賦值的過程當中會發生精度丟失,而咱們在使用jquery的data方法取得的自定義html屬性data-ordernumber
值會強制轉換成number類型,致使精度丟失。後端
而使用js原生的方法取dom節點的屬性時,獲取到的值都是string類型,這樣就不會出現number類型精度丟失的問題。安全
var orderNumber = $('.j_OrderNumber')[0].dataset.ordernumber // '9148368244236619'
typeof(orderNumber) === 'string' // true
複製代碼
因此咱們在經過自定義屬性取值number類型時,而且預期這些值會是相似訂單號這種會超過安全閾值的數值時,不要使用jquery的data方法。bash
可以被「安全」呈現的最大整數是Math.pow(2, 53) - 1
,即9007199254740992
。在ES6中被定義爲Number.MAX_SAFE_INTEGER
。dom
在開發環境,根據程序的特殊性,在有可能出現這種狀況時咱們應該杜絕掉超出安全閾值的大整數,並給出友好提示:
function isSafeInteger(num) {
return typeof(num) === 'number' && num % 1 == 0 && Math.abs(num) <= Math.pow(2, 53) -1;
}
複製代碼
在通常電商業務中比較常見出現大數值的場景也就是訂單號了,這類場景後端傳值給咱們的時候都強制包裝成string類型就會解決大多數的精度丟失問題了。
至於爲何會只有2的53次方-1的整數是安全的,能夠看阮神的關於數值的文章有詳細介紹
經典的 0.1 + 0.2 === 0.3 // false 問題
0.1 + 0.2 === 0.30000000000000004
對於這類數值比較問題,若是咱們已經知道了目標比較值,即若是咱們已經明確要與0.3
進行比較,咱們也能夠不須要獲得0.1+0.2的真實指望結果值(0.3),由於若是咱們要獲得0.3,還須要對0.1和0.2進行操做。常規解法:
(0.1 * 10 + 0.2 * 10) / 10 = 0.3
複製代碼
在「你不知道的javascript」一書中有提到一種判斷方法,設置一個偏差範圍值,一般也稱爲「機器精度」,對於javascript來講,這個值一般爲Math.pow(2, -53)
。
咱們能夠將須要比較的兩個值進行相減,再與這個機器精度進行比較,若是在偏差範圍內,咱們也視爲兩個值是相等的。
function numbersCloseEnoughToEqual(num1, num2) {
return Math.abs(num1 - num2) < Math.pow(2, -53);
}
複製代碼
對於電商業務來說,小數通過四則運算後可能會出現失去精度的問題,可是做爲展現來講咱們都會調用toFixed()
進行小數後幾位的約定,調用了這個方法後小數失去精度的問題也就迎刃而解了,不可能出現0.30000000000000004
這樣的數值。
因此在業務中有須要進行小數四則運算並會展現在頁面中,調用toFixed()
方法!
可是toFixed()
也有失去精度的時候!
1.335.toFixed(2)
// "1.33"
複製代碼
function toFixed(num, s) {
var times = Math.pow(10, s)
var des = num * times + 0.5
des = parseInt(des, 10) / times
return des + ''
}
複製代碼
本文來自二口南洋,有什麼須要討論的歡迎找我。