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

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

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

事实上,每个虚拟货币的精确程度小到小数点后 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],

  // 标识数值的指数

  e: 5,

  // 标识数值的正负

  s: 1

};

// '3086.27072845812345678'

let num2 = {

  c: [3086, 27072845812345, 67800000000000],

  e: 3,

  s: 1

};

 

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

高精度数值运算可以使用第 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 联系投稿~

相关文章: