4

技术内参 | 数据分析,如何解决精度丢失的问题?

 3 years ago
source link: https://www.sensorsdata.cn/blog/20180719-2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

技术内参 | 数据分析,如何解决精度丢失的问题?

神策小秘书 标签: 数据分析, 精度, 技术剖析, 产品, 运营 2018年07月19日

本文由神策数据技术大咖皮成投稿

谈到大数据技术,数据采集和计算是永远绕不开的话题,采集的准确度、计算的精度都是做数据分析的前提条件。

神策数据服务的客户覆盖互联网各个细分领域,其中不乏有做目前大火的虚拟货币的客户,比如涉及区块链和比特币领域的客户,从接触之初,他们对大数据工具的要求就极其严苛,当然,这与虚拟货币自身的特点也分不开。

事实上,每个虚拟货币的精确程度小到小数点后 4 位,大到小数点后 12 位。客户在分析用户每次成交的货币量,每日的人均成交货币量等指标,或者在分布分析中查看比持币的分布情况时,常常是需要精确到小数点后 8 位甚至更多。

因为虚拟货币的单价不同,更高的精确度可以让客户更加精准的细分出用户群,进而进行更深度的分析。高精度确保了分析结果的准确性,甚至决定了虚拟货币企业的核心竞争力。

所以,虚拟货币企业对精度的要求极高也不足为奇了。不过,在用 JavaScript 开发数据可视化产品时,会遇到数值的展现和计算丢失精度的问题,这是很多技术者想要解决的难题。下面我们从两方面来为你解决这个问题:

一、为什么会出现精度的丢失?

二、如何解决精度丢失问题?

下面我们先介绍下计算精度的背景:

1. 二进制小数

在十进制中,123.45 可以表示为 1 × 10² + 2 × 10¹ + 3 × 10⁰ + 4 × 10⁻¹ + 5 × 10⁻² = 123 ⁴⁵⁄₁₀₀,小数点的位置决定了数字的权重,左边的数是 10 的正幂,右边的数是 10 的负幂。

类似,二进制数 101.11 也可以表示为 1 × 2² + 0 × 2¹ + 1 × 2⁰ + 1 × 2⁻¹ + 1 × 2⁻² = 4 + 0 + 1 + ¹⁄₂ + ¹⁄₄ = 5 ³⁄₄,小数点向左移动一位相当于这个数被 2 除,小数点向右移动一位相当于这个数乘以 2。

在有限的长度下,十进制无法准确表示像 ¹⁄₃、⁵⁄₇ 这样的数,二进制也无法准确表示像 ¹⁄₅ 这样的数,增加数字的长度可以提高表示的精度。

2. IEEE 745 标准

IEEE 745 定义了在计算机中浮点数的表示及其运算的标准,标准指定了两种基本浮点格式:单精度和双精度,两种扩展浮点格式:单精度扩展和双精度扩展。

浮点格式是一种数据结构,用于指定包含浮点数的字段、这些字段的布局及其算术解释。浮点存储格式指定如何将浮点格式存储在内存中,具体选择哪种存储格式由实现工具决定,JavaScript 采用的是 IEEE 745 双精度浮点格式。

IEEE 浮点标准用 V = (−1)ˢ × 2ᴱ × M 的形式来表示一个数:

· 符号(sign)s 决定这个数是正数 s = 0 还是负数 s = 1。

· 指数(exponent)E 的作用是对浮点数的加权,权重是 2 的 E 次幂。

· 尾数(significand)M是一个二进制小数。

如下图所示,表示浮点数的位划分为三个字段:

· 一个符号位 s。

· k 位的指数字段 exp = eᵏ⁻¹ ... e¹e⁰ 编码指数 E。

· n 位的小数字段 frac = f⁻¹f⁻² ... f⁻ⁿ编码尾数 M。

图中给出了两种最常见的格式,32位的单精度格式:1 位符号位、k = 8 的指数字段、n = 23 的小数字段,64 位的双精度格式:1 位符号位、k = 11 的指数字段、n= 52 的小数字段。

3. IEEE 浮点格式转为十进制数

浮点格式要形成最终表示的值 V= 2ᴱ × M,需要对各个字段作如下处理:

· 指数 E = e - Bias,e表示为 eᵏ⁻¹ ... e¹e⁰,Bias 是一个等于 2ᵏ⁻¹ - 1 的偏移量。

· 小数字段 frac = f⁻¹f⁻² ... f⁻ⁿ,尾数 M = 1 + frac。

另外还有两种情况:

· 当指数字段全为 0 时,指数值是1 - Bias,尾数值是 M = frac。

· 当指数字段全为 1 时,小数字段全为 0 表示无穷,小数字段不等于 0 表示NaN。

下面以 8 位浮点格式为例介绍如何将其转为十进制数。

如:0 0110 110,其中有 k = 4 的指数字段和 n = 3 的小数字段,各个部分的处理如下:

· 偏移量 Bias = 2⁴⁻¹ - 1 = 7。

· 指数字段 e = 0 × 2³ + 1 × 2² + 1 × 2¹ + 0 ×2⁰ = 6。

· 指数 E = 6 - 7 = -1。

· 小数字段 frac = 1 × 2⁻¹ + 1 × 2⁻² + 0 × 2⁻³ = ³⁄₄。

· 尾数 M = 1 + ³⁄₄ = ⁷⁄₄。

· 最终值:V = 2ᴱ × M = 2⁻¹ × ⁷⁄₄ = ¹⁄₂ × ⁷⁄₄ = ⁷⁄₈ = 0.875。

上面是精度计算的一些背景知识,下面我们来解答前面提到的两个问题:

一、为什么会出现精度的丢失?

表示方法限制了浮点数的范围和精度,所以浮点运算只能近似的表示实数运算。

IEEE 浮点格式定义了 4 种不同的舍入方式:

· Round-to-even,向偶数舍入,将数字向上或向下舍入,使得结果的最低有效数字是偶数,如以十进制数为例:1.5 和 2.5 都将舍入为 2。

· Round-toward-zero,向零舍入,把正数向下舍入,负数向上舍入。

· Round-down,向下舍入,把正数、负数都向下舍入。

· Round-up,向上舍入,把正数、负数都向上舍入。

JavaScript 中的浮点数有两种来源,由服务端返回的数据或者通过运算产生的结果,从服务端返回数据到前端展现会经过下面几个环节:

在这个过程中,需要解决 JSON 解析丢失精度的问题、高精度浮点数运算的问题、高精度浮点数展示的问题。

在 JavaScript 中通常会用 number 数据类型来保存数值、进行数值的运算,但由于 IEEE 浮点格式的限制,用 number 类型保存一个大浮点数时,精度会丢失。

const num = 106119.43448475052345678

// 106119.43448475053

同样,我们常用的JSON.parse 方法解析 JSON 字符串时,大浮点数的精度也会丢失。

const data = JSON.parse('{"values":[106119.43448475052345678,755180144.3253138888456,3086.27072845812345678]}');

// values: [

// 106119.43448475053,

// 755180144.3253139,

// 3086.2707284581234

二、如何解决精度丢失问题?

要确保从服务端获取的大浮点数精度不丢失,就不能用 JSON.parse 方法来解析 HTTP Response Body 里的 JSON 字符串,也不能直接用 number 类型来保存数值。

解析 JSON 时可以把数值保存为 string 类型:

const data = {

values: [

'106119.43448475052345678',

'755180144.3253138888456',

'3086.27072845812345678'

JSON 解析可以用第 3 方库,如:https://github.com/sidorares/json-bigint

要对高精度浮点数进行运算,首先要将其转为便于运算的结构,如:

// '106119.43448475052345678'

let num = {

// 数值拆成数组

c: [106119, 43448475052345, 67800000000000],

// 标识数值的指数

// 标识数值的正负

// '3086.27072845812345678'

let num2 = {

c: [3086, 27072845812345, 67800000000000],

转为如上结构后,再对数组中的各段数值进行运算。

高精度数值运算可以使用第 3 方库,如:http://mikemcl.github.io/bignumber.js/

高精度浮点数在网页中的展示有所不同,具体如下:

浮点数在页面上的展示通常有两种方式,创建为一个文本节点、通过 Canvas 或 SVG 画成图表。

创建为文本节点来展示没有什么问题,如:

document.createTextNode('106119.43448475052345678');

let div =document.createElement('div');

div.innerText = '106119.43448475052345678';

画图表通常会用到第 3 方库,如:ECharts、G2 等,各种图表的具体实现上会依赖数值的比较、四则运算等,目前 ECharts、G2 还没有对大浮点数的支持。

看完上文是不是学到很多?

想要深入了解的小伙伴,欢迎来撩神策数据的技术小哥哥,各个身怀绝技哦!

PS:此篇文章就是技术小哥哥皮成的经验分享

与凤凰同飞,必是俊鸟;与虎狼同行,必是猛兽!

来神策数据,你不变优秀,我输一包辣条!

对,我们正在寻找优秀的你!

快来加入我们吧!

热招岗位

大数据前端工程师

JS 数据采集方向:

1、负责神策 JS SDK 的开发,维护,客户支持;

2、负责前端相关数据采集方案的调研和开发,比如微信小程序,支付宝等。

数据可视化方向:

1、负责数据分析产品的 Web 前端研发;

2、与产品设计师一起,优化整个产品的使用流程,为使用者提供更好的数据可视化效果;

3、负责前端新技术的探索与实践。

我们的挑战:

1、单页面复杂 Web 应用,需要管理 10 万行业务代码,代码量还在快速增长中;

2、与大量数据打交道,需要对数据业务有深层次的理解;

3、服务大量客户,产品快速迭代的同时,要让系统在健康的轨道上发展。

对工程师的要求:

1、熟练掌握 HTML/CSS/JavaScript 等前端基础技术;

2、能写出高质量的代码,有良好的代码风格、了解各种设计模式;

3、React/Vue/Angular 技术栈至少掌握其中一种,对背后的设计思路应该比较了解,读源码应该没什么困难;

4、对写代码之外的事情能熟练运用各种工程工具处理,对工具背后的工作原理应该有一些了解;

5、10 万行代码在浏览器里运行,性能方面是个挑战,怎么分析、优化性能应该有一定的了解;

6、很好的业务理解能力,与大数据打交道,需要理解的不只是页面上有哪些 UI 组件。

关于简历,我们想看到的:

1、做过的最牛的项目,用到了什么技术、实现了什么功能、有什么收获。

2、写过的最牛的代码。

如果想用前端技术做一些有趣、有挑战的事情,欢迎给我们简历!

如果没有开发过复杂 Web 应用,但对自己的学习能力足够自信,欢迎给我们简历!

划重点

【工作地点】北京

【应聘方式】简历可发送至邮箱

[email protected]

Hr 小姐姐都很美哦!欢迎来撩~

更多岗位可在神策数据官网了解

参考资料:

·IEEE 754,https://en.wikipedia.org/wiki/IEEE_754

·Numerical Computation Guide,https://docs.oracle.com/cd/E19957-01/806-3568/ncgTOC.html

· 《深入理解计算机系统》

神策数据开“技术内参”新栏目啦

有内涵的人就该发光!

你有技术干货,我有曝光渠道!

欢迎给我们投稿,好的文章不仅会在神策数据公众号发布,也会在神策数据新媒体矩阵发布,让你 blingbling!

投稿方式:

将文章发送邮箱,我们会在 1-2 个工作日给您反馈!

邮箱:[email protected]

如邮箱未及时回复可加微信 wafree 联系投稿~

更多数据分析干货和案例,可以关注“神策数据”公众号了解~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK