JSON實戰拾遺之數字精度
正如《JSON實戰》書中所言 —— “JSON已經成為 RESTful 介面設計中的事實標準”,作為一個寫程式的,我們很難避免與JSON打交道。
JSON從本質上講就是一類字串,所以在第二章(“在 JavaScript 中使用 JSON”)裡一開始就向我們介紹了JSON的序列化 / 反序列化操作。用JSON.stringify() 將資訊序列化為 JSON(字串);用JSON.parse() 將 JSON 反序列化為 JavaScript 可以理解的資料結構。
例如我們在node.js控制檯或者chrome控制檯裡輸入:
JSON.stringify({"數字": 12345678901234567890})
會得到:
'{"數字":12345678901234567000}'
再例如,輸入:
JSON.parse('{"數字":12345678901234567890}')
會得到
{ '數字': 12345678901234567000 }
看看,JSON的序列化和反序列化就是如此簡單!
但是,如果我們仔細的看看序列化和反序列化的結果,就會喊出WTF —— 我明明輸入的數字是 12345678901234567890,怎麼得到的卻是12345678901234567000 ??
為什麼會出現這樣的結果?其實當你用的數字比較小的時候,是碰不上這個問題的,只有當你用的數字足夠大(超過 Number.MAX_SAFE_INTEGER),或者足夠小(小於 Number.MIN_SAFE_INTEGER)的時候,這個問題才會浮現,按照MDN上的描述是這樣的 ——
形成這個數字的原因是 JavaScript 在 IEEE 754中使用double-precision floating-point format numbers 作為規定。在這個規定中能安全的表示數字的範圍在-(253 - 1) 到 253 - 1之間.
怎麼辦?
是否要解決這個問題取決於你的業務場景。
如果你確信你的程式不會涉及到上面那麼大的數,那就放心的使用JSON.parse和JSON.stringify好了(真是廢話!?)
後端之間傳遞
如果你只是在後端使用JSON,那麼選擇方案還是挺多的,比如node.js裡有一些庫 lossless-json、json-bigint 可以解決精度的問題。再比如python就不存在這個問題(所以沒人用javascript去做科學計算啊 ?)。
前後端之間傳遞
如果要在前端進行一些互動,比如讓使用者輸入數字再轉成JSON傳遞給後端,那麼問題就出現了:怎麼保證前端輸入的數字在前端被轉為JSON的時候不走樣呢?我找了一圈之後發現答案其實挺簡單,那就是你把在前端輸入的數字就當成一個字串,轉成JSON就不會走樣。
在JS控制檯裡輸入:
JSON.stringify({"數字": "12345678901234567890"})
得到:
'{"數字":"12345678901234567890"}'
當這個寫成字串的JSON被傳遞到後端以後,根據後端用的語言不同,有不同的處理方法。
例如:
Python
python做後端的只要直接把數字字串轉成需要的數字型別即可,例如:
int(json.loads('{"數字":"12345678901234567890"}')['數字'])
得到
12345678901234567890
Node.js
可以利用 decimal.js 、bignumber.js 這樣的庫把 字串轉為"big number" 再進行計算:
var BigNumber = require('bignumber.js');
數字 = BigNumber('12345678901234567890');
數字.plus('987654321')
小結
JSON在Javascript的序列化與反序列化當中存在的數字精度其實是一個比較常見的問題,如果你在使用JS處理JSON數字時得到了一些莫名其妙的結果,請考慮一下是不是遇到了這個問題。
相關文章
- docker拾遺-之再入坑Docker
- mongoose 拾遺Go
- 【JS拾遺】函式的引數JS函式
- Zepto核心模組之工具方法拾遺
- 【演算法拾遺】最大數和最小數演算法
- 2萬字 | 前端基礎拾遺90問前端
- 【演算法拾遺】最大公約數演算法
- 前端技能拾遺前端
- Linux拾遺Linux
- Java Web 拾遺JavaWeb
- [MASM拾遺]OffsetASM
- Java深海拾遺系列(5)--- 精度計算中的BigDecimal,double和floatJavaDecimal
- 物件導向拾遺物件
- 【java web】--Ajax拾遺JavaWeb
- C語言拾遺C語言
- golang拾遺:嵌入型別Golang型別
- Unix廣告拾遺 by Dennis Ritchie
- Objective-C Runtime 執行時之六:拾遺Object
- [C#.NET 拾遺補漏]10:理解 volatile 關鍵字C#
- Vue.js基礎拾遺Vue.js
- PHP 使用 Kafka 安裝拾遺PHPKafka
- AS拾遺--向PM學習二
- golang拾遺:指標和介面Golang指標
- 【演算法拾遺】階乘演算法
- 【Java學習筆記】拾遺Java筆記
- C++標準庫::拾遺C++
- 數字遺產價值量在增大 網路“數字遺產”如何繼承?繼承
- golang拾遺:實現一個不可複製型別Golang型別
- 課時39:類與物件:拾遺物件
- C#拾遺補闕【01】:字串C#字串
- 閉包拾遺 & 垃圾回收機制
- C++基礎::拾遺&&瑣碎C++
- [C#.NET 拾遺補漏]06:單例模式實佳實踐C#單例模式
- MindSpore模型精度調優實戰:如何更快定位精度問題模型
- 【演算法拾遺】大數相加(不開闢額外空間)演算法
- 前端拾遺--javascript-ES6基礎前端JavaScript
- [Go]Go 語言基礎拾遺(一)Go
- MIT 6.824拾遺(一)聊聊basic-paxosMIT