詭異的數值精度丟失

問題描述

在一次開發列表編輯頁面時發現一個詭異的問題,列表展現的數據和回顯的數據不同,第一反應是接口出錯了,查看了一下ajax請求,好像確實是這樣:ajax

列表請求接口: npm

列表查詢請求

數據回顯請求接口:瀏覽器

數據回顯請求
數據回顯請求

很明顯列表數據和回顯數據不一致,因而就找服務端同窗排查接口問題。服務端同窗反饋從日誌上看是沒問題的,對比他們日誌,發現日誌打印的數據和界面展現的不一致,詭異的事情就這樣發生了。更詭異的是直接在瀏覽器地址欄訪問這個接口,返回的數據和日誌的同樣,和我界面展現的也不一致。bash

地址欄請求數據

看樣子應該是我項目中請求對數據作了處理,回過頭再來看請求的數據,對比發現 driverId 數值,初步定爲是由於數值類型精度丟失。spa

let num = 1001379549335920640
// 1001379549335920600
複製代碼

精度丟失緣由

js數值類型遵循IEEE 754規範,佔用64位,日誌

s eeeeeee eeee ffff ffffffff ffffffff  ffffffff ffffffff ffffffff ffffffff
複製代碼

從左起code

  • 1位用來表示符號
  • 11位用來表示指數
  • 52表示尾數

這種表示方法直接致使了精度丟失,當數值沒法用二進制表示時,就會採起0舍1入。cdn

浮點數和大整數都會出現精度丟失。當大整數尾數大於 2^52 = 9007199254740992 時就可能精度丟失blog

9007199254740992     >> 10000000000000...000 // 共計 53 個 0
9007199254740992 + 1 >> 10000000000000...001 // 中間 52 個 0
9007199254740992 + 2 >> 10000000000000...010 // 中間 51 個 0
複製代碼
9007199254740992 + 1 // 丟失
// 9007199254740992 
9007199254740992 + 2 // 未丟失
// 9007199254740994
9007199254740992 + 3 // 丟失
// 9007199254740996
9007199254740992 + 4 // 未丟失
// 9007199254740996
複製代碼

解決方案

  • 若是數值自己不參與運算,能夠換成字符類型
  • 若是數值在常規範圍內,能夠先把小數放大到整數,運算後在縮小原來整數倍
// 0.1 + 0.2
(0.1*10 + 0.2*10) / 10 == 0.3 // true
複製代碼
  • 若是對精度有嚴格要求,能夠本身實現大數運算,或藉助npm包(例如big.js
相關文章
相關標籤/搜索