技术内参 | 数据分析,如何解决精度丢失的问题?
?
?
本文由神策數(shù)據(jù)技術大咖皮成投稿
談到大數(shù)據(jù)技術,數(shù)據(jù)采集和計算是永遠繞不開的話題,采集的準確度、計算的精度都是做數(shù)據(jù)分析的前提條件。
神策數(shù)據(jù)服務的客戶覆蓋互聯(lián)網(wǎng)各個細分領域,其中不乏有做目前大火的虛擬貨幣的客戶,比如涉及區(qū)塊鏈和比特幣領域的客戶,從接觸之初,他們對大數(shù)據(jù)工具的要求就極其嚴苛,當然,這與虛擬貨幣自身的特點也分不開。
事實上,每個虛擬貨幣的精確程度小到小數(shù)點后 4 位,大到小數(shù)點后 12 位。客戶在分析用戶每次成交的貨幣量,每日的人均成交貨幣量等指標,或者在分布分析中查看比持幣的分布情況時,常常是需要精確到小數(shù)點后 8 位甚至更多。
因為虛擬貨幣的單價不同,更高的精確度可以讓客戶更加精準的細分出用戶群,進而進行更深度的分析。高精度確保了分析結果的準確性,甚至決定了虛擬貨幣企業(yè)的核心競爭力。
所以,虛擬貨幣企業(yè)對精度的要求極高也不足為奇了。不過,在用 JavaScript 開發(fā)數(shù)據(jù)可視化產品時,會遇到數(shù)值的展現(xiàn)和計算丟失精度的問題,這是很多技術者想要解決的難題。下面我們從兩方面來為你解決這個問題:
一、為什么會出現(xiàn)精度的丟失?
二、如何解決精度丟失問題?
下面我們先介紹下計算精度的背景:
1. 二進制小數(shù)
在十進制中,123.45 可以表示為 1 × 102 + 2 × 101 + 3 × 10? + 4 × 10?1 + 5 × 10?2 = 123 ??????,小數(shù)點的位置決定了數(shù)字的權重,左邊的數(shù)是 10 的正冪,右邊的數(shù)是 10 的負冪。
類似,二進制數(shù) 101.11 也可以表示為 1 × 22 + 0 × 21 + 1 × 2? + 1 × 2?1 + 1 × 2?2 = 4 + 0 + 1 + 1?? + 1?? = 5 3??,小數(shù)點向左移動一位相當于這個數(shù)被 2 除,小數(shù)點向右移動一位相當于這個數(shù)乘以 2。
在有限的長度下,十進制無法準確表示像 1??、??? 這樣的數(shù),二進制也無法準確表示像 1?? 這樣的數(shù),增加數(shù)字的長度可以提高表示的精度。
2. IEEE 745 標準
IEEE 745 定義了在計算機中浮點數(shù)的表示及其運算的標準,標準指定了兩種基本浮點格式:單精度和雙精度,兩種擴展浮點格式:單精度擴展和雙精度擴展。
浮點格式是一種數(shù)據(jù)結構,用于指定包含浮點數(shù)的字段、這些字段的布局及其算術解釋。浮點存儲格式指定如何將浮點格式存儲在內存中,具體選擇哪種存儲格式由實現(xiàn)工具決定,JavaScript 采用的是 IEEE 745 雙精度浮點格式。
IEEE 浮點標準用 V = (?1)? × 2? × M 的形式來表示一個數(shù):
· 符號(sign)s 決定這個數(shù)是正數(shù) s = 0 還是負數(shù) s = 1。
· 指數(shù)(exponent)E 的作用是對浮點數(shù)的加權,權重是 2 的 E 次冪。
· 尾數(shù)(significand)M是一個二進制小數(shù)。
如下圖所示,表示浮點數(shù)的位劃分為三個字段:
· 一個符號位 s。
· k 位的指數(shù)字段 exp = e??1 ... e1e? 編碼指數(shù) E。
· n 位的小數(shù)字段 frac = f?1f?2 ... f??編碼尾數(shù) M。
圖中給出了兩種最常見的格式,32位的單精度格式:1 位符號位、k = 8 的指數(shù)字段、n = 23 的小數(shù)字段,64 位的雙精度格式:1 位符號位、k = 11 的指數(shù)字段、n= 52 的小數(shù)字段。
3. IEEE 浮點格式轉為十進制數(shù)
浮點格式要形成最終表示的值 V= 2? × M,需要對各個字段作如下處理:
·? 指數(shù) E = e - Bias,e表示為 e??1 ... e1e?,Bias 是一個等于 2??1 - 1 的偏移量。
· 小數(shù)字段 frac = f?1f?2 ... f??,尾數(shù) M = 1 + frac。
另外還有兩種情況:
· 當指數(shù)字段全為 0 時,指數(shù)值是1 - Bias,尾數(shù)值是 M = frac。
· 當指數(shù)字段全為 1 時,小數(shù)字段全為 0 表示無窮,小數(shù)字段不等于 0 表示NaN。
下面以 8 位浮點格式為例介紹如何將其轉為十進制數(shù)。
如:0 0110 110,其中有 k = 4 的指數(shù)字段和 n = 3 的小數(shù)字段,各個部分的處理如下:
· 偏移量 Bias = 2??1 - 1 = 7。
· 指數(shù)字段 e = 0 × 23 + 1 × 22 + 1 × 21 + 0 ×2? = 6。
· 指數(shù) E = 6 - 7 = -1。
· 小數(shù)字段 frac = 1 × 2?1 + 1 × 2?2 + 0 × 2?3 = 3??。
· 尾數(shù) M = 1 + 3?? = ???。
· 最終值:V = 2? × M = 2?1 × ??? = 1?? × ??? = ??? = 0.875。
上面是精度計算的一些背景知識,下面我們來解答前面提到的兩個問題:
一、為什么會出現(xiàn)精度的丟失?
表示方法限制了浮點數(shù)的范圍和精度,所以浮點運算只能近似的表示實數(shù)運算。
IEEE 浮點格式定義了 4 種不同的舍入方式:
· Round-to-even,向偶數(shù)舍入,將數(shù)字向上或向下舍入,使得結果的最低有效數(shù)字是偶數(shù),如以十進制數(shù)為例:1.5 和 2.5 都將舍入為 2。
· Round-toward-zero,向零舍入,把正數(shù)向下舍入,負數(shù)向上舍入。
· Round-down,向下舍入,把正數(shù)、負數(shù)都向下舍入。
· Round-up,向上舍入,把正數(shù)、負數(shù)都向上舍入。
JavaScript 中的浮點數(shù)有兩種來源,由服務端返回的數(shù)據(jù)或者通過運算產生的結果,從服務端返回數(shù)據(jù)到前端展現(xiàn)會經(jīng)過下面幾個環(huán)節(jié):
在這個過程中,需要解決 JSON 解析丟失精度的問題、高精度浮點數(shù)運算的問題、高精度浮點數(shù)展示的問題。
在 JavaScript 中通常會用 number 數(shù)據(jù)類型來保存數(shù)值、進行數(shù)值的運算,但由于 IEEE 浮點格式的限制,用 number 類型保存一個大浮點數(shù)時,精度會丟失。
const num = 106119.43448475052345678
// 106119.43448475053
?
同樣,我們常用的JSON.parse 方法解析 JSON 字符串時,大浮點數(shù)的精度也會丟失。
const data = JSON.parse('{"values":[106119.43448475052345678,755180144.3253138888456,3086.27072845812345678]}');
// {
//? ?values: [
//? ? ?106119.43448475053,
//? ? ?755180144.3253139,
//? ? ?3086.2707284581234
//? ?]
// }
二、如何解決精度丟失問題?
要確保從服務端獲取的大浮點數(shù)精度不丟失,就不能用 JSON.parse 方法來解析 HTTP Response Body 里的 JSON 字符串,也不能直接用 number 類型來保存數(shù)值。
解析 JSON 時可以把數(shù)值保存為 string 類型:
const data = {
? values: [
? ? '106119.43448475052345678',
? ? '755180144.3253138888456',
? ? '3086.27072845812345678'
? ]
};
?
JSON 解析可以用第 3 方庫,如:https://github.com/sidorares/json-bigint
要對高精度浮點數(shù)進行運算,首先要將其轉為便于運算的結構,如:
// '106119.43448475052345678'
let?num = {
? // 數(shù)值拆成數(shù)組
? c: [106119, 43448475052345, 67800000000000],
? // 標識數(shù)值的指數(shù)
? e: 5,
? // 標識數(shù)值的正負
? s: 1
};
// '3086.27072845812345678'
let?num2 = {
? c: [3086, 27072845812345, 67800000000000],
? e: 3,
? s: 1
};
?
轉為如上結構后,再對數(shù)組中的各段數(shù)值進行運算。
高精度數(shù)值運算可以使用第 3 方庫,如:http://mikemcl.github.io/bignumber.js/
高精度浮點數(shù)在網(wǎng)頁中的展示有所不同,具體如下:
浮點數(shù)在頁面上的展示通常有兩種方式,創(chuàng)建為一個文本節(jié)點、通過 Canvas 或 SVG 畫成圖表。
創(chuàng)建為文本節(jié)點來展示沒有什么問題,如:
document.createTextNode('106119.43448475052345678');
// 或
let?div =document.createElement('div');
div.innerText = '106119.43448475052345678';
畫圖表通常會用到第 3 方庫,如:ECharts、G2 等,各種圖表的具體實現(xiàn)上會依賴數(shù)值的比較、四則運算等,目前 ECharts、G2 還沒有對大浮點數(shù)的支持。
看完上文是不是學到很多?
想要深入了解的小伙伴,歡迎來撩神策數(shù)據(jù)的技術小哥哥,各個身懷絕技哦!
PS:此篇文章就是技術小哥哥皮成的經(jīng)驗分享
與鳳凰同飛,必是俊鳥;與虎狼同行,必是猛獸!
來神策數(shù)據(jù),你不變優(yōu)秀,我輸一包辣條!
對,我們正在尋找優(yōu)秀的你!
快來加入我們吧!
熱招崗位
大數(shù)據(jù)前端工程師
?
JS 數(shù)據(jù)采集方向:
1、負責神策 JS SDK 的開發(fā),維護,客戶支持;
2、負責前端相關數(shù)據(jù)采集方案的調研和開發(fā),比如微信小程序,支付寶等。
?
數(shù)據(jù)可視化方向:
1、負責數(shù)據(jù)分析產品的 Web 前端研發(fā);
2、與產品設計師一起,優(yōu)化整個產品的使用流程,為使用者提供更好的數(shù)據(jù)可視化效果;
3、負責前端新技術的探索與實踐。
我們的挑戰(zhàn):
1、單頁面復雜 Web 應用,需要管理 10 萬行業(yè)務代碼,代碼量還在快速增長中;
2、與大量數(shù)據(jù)打交道,需要對數(shù)據(jù)業(yè)務有深層次的理解;
3、服務大量客戶,產品快速迭代的同時,要讓系統(tǒng)在健康的軌道上發(fā)展。
對工程師的要求:
1、熟練掌握 HTML/CSS/JavaScript 等前端基礎技術;
2、能寫出高質量的代碼,有良好的代碼風格、了解各種設計模式;
3、React/Vue/Angular 技術棧至少掌握其中一種,對背后的設計思路應該比較了解,讀源碼應該沒什么困難;
4、對寫代碼之外的事情能熟練運用各種工程工具處理,對工具背后的工作原理應該有一些了解;
5、10 萬行代碼在瀏覽器里運行,性能方面是個挑戰(zhàn),怎么分析、優(yōu)化性能應該有一定的了解;
6、很好的業(yè)務理解能力,與大數(shù)據(jù)打交道,需要理解的不只是頁面上有哪些 UI 組件。
關于簡歷,我們想看到的:
1、做過的最牛的項目,用到了什么技術、實現(xiàn)了什么功能、有什么收獲。
2、寫過的最牛的代碼。
如果想用前端技術做一些有趣、有挑戰(zhàn)的事情,歡迎給我們簡歷!
如果沒有開發(fā)過復雜 Web 應用,但對自己的學習能力足夠自信,歡迎給我們簡歷!
?劃重點?
【工作地點】北京
【應聘方式】簡歷可發(fā)送至郵箱?
hr@sensorsdata.cn
?
Hr 小姐姐都很美哦!歡迎來撩~
更多崗位可在神策數(shù)據(jù)官網(wǎng)了解
參考資料:
·IEEE 754,https://en.wikipedia.org/wiki/IEEE_754
·Numerical Computation Guide,https://docs.oracle.com/cd/E19957-01/806-3568/ncgTOC.html
· 《深入理解計算機系統(tǒng)》
?!神策數(shù)據(jù)開“技術內參”新欄目啦 !
有內涵的人就該發(fā)光!
你有技術干貨,我有曝光渠道!
歡迎給我們投稿,好的文章不僅會在神策數(shù)據(jù)公眾號發(fā)布,也會在神策數(shù)據(jù)新媒體矩陣發(fā)布,讓你 blingbling!
投稿方式:
將文章發(fā)送郵箱,我們會在 1-2 個工作日給您反饋!
郵箱:wangyajun@sensorsdata.cn?
如郵箱未及時回復可加微信?wafree?聯(lián)系投稿~
總結
以上是生活随笔為你收集整理的技术内参 | 数据分析,如何解决精度丢失的问题?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 捞月狗签约神策数据 数据赋能打造全球玩家
- 下一篇: 金融实战篇:最佳数据驱动之城商行