最近生产上出现了一次JS精度丢失的现象,这个问题解决起来不是很难, 但是对于为什么会出现这种情况进行了思考,得到了一些体会,Mark 一下。
首先:这个请求是Ajax请求,如果正常的post或者get 请求是不会出现这种问题,原因后续会讲。
首先:先来说下JS精度丢失是什么,总体来说,就是JS表示浮点数的时候会出现误差。包含两类问题:
1、使用JS进行浮点数计算,得到错误的结果。
2、Ajax请求后端返回的数值类型的值大于JS的表示范围(本次产线的场景。)
浮点数计算。
经典的 0.1+0.2!=0.3
产生这个问题的原因是及计算机是二进制的方式保存浮点数,同时又有位数的限制,导致了浮点数精度的缺失。
几乎所有的编程语言都采用IEEE-745标准表示浮点数,这是一种对于实数的近似值数值表现,并不能精确的表示类似0.1这样 的简单的数字。为什么呢?
浮点数:简单来讲浮点数就是小数点逻辑上不固定,跟它对应的就是定点数,小数点位置固定。比如oracle中的Number(5,2),意思就是总长度5位,其中整数3位,小数两位。小数点固定。这种表达方式的缺点就是表达范围和精度比较局限。不利于同时表达特大或者特小的数字。而浮点数却能很好的规避这个缺点,浮点数使用一个有效数字,一个基数,一个指数,以及一个表示正负的符号来表达实数,通过指数达到浮动小数点的效果,例如1.234x10^1和 1.234x10^2 。
IEEE-745:计算机的存储方式是二进制,因此使用计算机表示浮点数的时候基数就变成了2,可以理解为2进制的科学计数法,(尾数用原码;阶码用“移码”;基为2)。
IEEE-745标准定义的浮点数据格式为:(其实有32bit 的单精度和64 bit的双精度,本例只说双精度)
值(尾数)Fraction | 指数(阶码)Exponent | 符号 Sign |
---|---|---|
52bits(0-51) | 11bits(52-62) | 1bit(63) |
有效数字 | 表示小数点在数据中的位置 | 1–负数;0–正数 |
按照上面的指数表示方法,一个二进制的浮点数会有不同的表示:
0.00101(2) = 1.01×2−3 = 10.1×2−4
为了提高数据的表示精度同时保证数据表示的唯一性,需要对浮点数做规格化处理。在计算机内,对非0值的浮点数,要求尾数域的最高有效位应为1,称满足这种表示要求的浮点数为规格化表示:把不满足这一表示要求的尾数,变成满足这一要求的尾数的操作过程,叫作浮点数的规格化处理,可以通过尾数移位和修改阶码实现。
即:浮点数的格式变为: