js數值精度

19年目標:消滅英語!我新開了一個公衆號記錄一個程序員學英語的歷程javascript

有提高英語訴求的小夥伴能夠關注公衆號:csenglish 程序員學英語,天天花10分鐘交做業,跟我一塊兒學英語吧html


近期在項目中有出現大數值的訂單號9148368244236619在調用接口時自動變成9148368244236620的狀況,致使請求失誤。本文特地總結了出現這種狀況的緣由,以及js精度相關的狀況。前端

jquery[.data()]方法

在本次案例中,訂單號是後端同步渲染到頁面上的,java

<div class="j_OrderNumber" data-ordernumber="9148368244236619"></div>
複製代碼

呈如今頁面上的訂單號數值沒有問題9148368244236619,前端此時想獲取到這個訂單號:jquery

var orderNumber = $('.j_OrderNumber).data('ordernumber'); // 9148368244236620 typeof(orderNumber) === 'number' // true 複製代碼

在這個取值賦值的過程當中,超過安全值的大數值發生了變化,從9148368244236619變成了91483682442366120,這裏的問題主要出如今jquerydata方法。程序員

超過安全值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_INTEGERdom

在開發環境,根據程序的特殊性,在有可能出現這種狀況時咱們應該杜絕掉超出安全閾值的大整數,並給出友好提示:

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 + ''
}
複製代碼

小數的四則運算

參考

本文來自二口南洋,有什麼須要討論的歡迎找我。

相關文章
相關標籤/搜索