JavaScript 中小数和大整数的精度丢失
来源:互联网 发布:明礼仪知荣辱演讲稿 编辑:程序博客网 时间:2024/04/27 07:20
http://blog.php1.cn/article/details/2960
先来看两个问题:
0.1 + 0.2 == 0.3; // false9999999999999999 == 10000000000000000; // true
第一个问题是小数的精度问题,在业界不少博客里已有讨论。第二个问题,去年公司有个系统的数据库在做数据订正时,发现有部分数据重复的诡异现象。本文将从规范出发,对上面的问题做个小结。
最大整数
JavaScript 中的数字是用 IEEE 754 双精度 64 位浮点数 来存储的,其格式为:
s x m x 2^e
s 是符号位,表示正负。 m 是尾数,有 52 bits. e 是指数,有 11 bits. 在 ECMAScript 规范 里有给出 e 的范围为 [-1074, 971]. 这样,很容易推导出 JavaScript 能表示的最大整数为:
1 x (2^53 - 1) x 2^971 = 1.7976931348623157e+308
这个值正是 Number.MAX_VALUE
同理可推导出 Number.MIN_VALUE 的值为:
1 x 1 x 2^(-1074) = 5e-324
注意 MIN_VALUE 表示最接近 0 的正数,而不是最小的数。最小的数是 -Number.MAX_VALUE
小数的精度丢失
JavaScript 的数字都是双精度浮点数,在计算机里用二进制存储。当有效位数超过 52 位时,会存在精度丢失。比如:
十进制 0.1 的二进制为 0.0 0011 0011 0011 … (循环 0011)十进制 0.2 的二进制为 0.0011 0011 0011 … (循环 0011)0.1 + 0.2 相加可表示为: e = -4; m = 1.10011001100...1100(52 位) + e = -3; m = 1.10011001100...1100(52 位)--------------------------------------------- e = -3; m = 0.11001100110...0110 + e = -3; m = 1.10011001100...1100--------------------------------------------- e = -3; m = 10.01100110011...001--------------------------------------------- = 0.01001100110011...001 = 0.30000000000000004(十进制)
根据上面的演算,还可以得出一个结论:当十进制小数的二进制表示的有限数字不超过 52 位时,在 JavaScript 里是可以精确存储的。比如:
0.05 + 0.005 == 0.055 // true
进一步的规律,比如:
0.05 + 0.2 == 0.25 // true
0.05 + 0.9 == 0.95 // false
需要考虑 IEEE 754 的 Rounding modes, 有兴趣的可进一步研究。
大整数的精度丢失
这个问题鲜有人提及。首先得弄清楚问题是什么:
1. JavaScript 能存储的最大整数是什么?
该问题前面已回答,是 Number.MAX_VALUE, 非常大的一个数。
2. JavaScript 能存储的且不丢失精度的最大整数是什么?
根据 s x m x 2^e
, 符号位取正,52 位尾数全填充 1, 指数 e 取最大值 971, 显然,答案依旧是 Number.MAX_VALUE.
我们的问题究竟是什么呢?回到起始代码:
9999999999999999 == 10000000000000000; // true
很明显,16 个 9 还远远小于 308 个 10. 这个问题与 MAX_VALUE 没什么关系,还得归属到尾数 m 只有 52 位上来。
可以用代码来描述:
var x = 1; // 为了减少运算量,初始值可以设大一点,比如 Math.pow(2, 53) - 10while(x != x + 1) x++;// x = 9007199254740992 即 2^53
也就是说,当 x 小于等于 2^53 时,可以确保 x 的精度不会丢失。当 x 大于 2^53 时,x 的精度有可能会丢失。比如:
x 为 2^53 + 1 时,其二进制表示为:10000000000...001 (中间共有 52 个 0)用双精度浮点数存储时:e = 1; m = 10000..00(共 52 个 0,其中 1 是 hidden bit)显然,这和 2^53 的存储是一样的。
按照上面的思路可以推出,对于 2^53 + 2, 其二进制为 100000…0010(中间 51 个 0),也是可以精确存储的。
规律:当 x 大于 2^53 且二进制有效位数大于 53 位时,就会存在精度丢失。这和小数的精度丢失本质上是一样的。
hidden bit 可参考:A tutorial about Java double type.
小结
小数和大整数的精度丢失,并不仅仅在 JavaScript 中存在。严格来说,使用了IEEE 754 浮点数格式来存储浮点类型的任何编程语言(C/C++/C#/Java 等等)都存在精度丢失问题。在 C#、Java 中,提供了 Decimal、BigDecimal 封装类来进行相应的处理,才避开了精度丢失。
注:ECMAScript 规范中,已有 decimal proposal,但目前尚未被正式采纳。
最后考考大家:
Number.MAX_VALUE + 1 == Numer.MAX_VALUE;Number.MAX_VALUE + 2 == Numer.MAX_VALUE;...Number.MAX_VALUE + x == Numer.MAX_VALUE;Number.MAX_VALUE + x + 1 == Infinity;...Number.MAX_VALUE + Number.MAX_VALUE == Infinity;// 问题:// 1. x 的值是什么?// 2. Infinity - Number.MAX_VALUE == x + 1; 是 true 还是 false ?
参考资料
- Wikipedia: Float point
- ES5: The Number Type
- Javascript – MAX_INT: Number Limits
- Maxinum number
- 关于 JavaScript 计算精度丢失的问题
- JavaScript 中小数和大整数的精度丢失
- JavaScript 中小数和大整数的精度丢失
- js中小数运算精度丢失问题
- Flex 中小数精度问题
- Python中小整数对象池和大整数对象池
- python中运算过程中小数精度的控制
- 浮点数精度丢失的原因
- 浮点数精度丢失问题的解决
- javascript(js)小数精度丢失的解决方案
- javascript小数精度丢失的完美解决方法
- javaScript中小数相乘的问题
- 在大数组中找重复和丢失的整数
- js中小数转换整数
- js中小数转换整数
- js中小数转换整数
- js中小数转换整数
- 定义SQL查询结果中小数精度
- freemaker中小数展示为整数的问题
- mysql5.6主从复制新特性测试
- KMP算法(Python实现)
- python数组查找算法---bisect二分查找插入
- Java之美[从菜鸟到高手演变]之设计模式
- Sysbench参数中文解释
- JavaScript 中小数和大整数的精度丢失
- Bootstrap Paginator分页插件+ajax 实现动态无刷新分页
- ELMAH--Using HTTP Modules and Handlers to Create Pluggable ASP.NET Components 77 out of 90 rated th
- 阿里巴巴集团全资收购UC优视
- twitter storm源码走读之8 -- TridentTopology创建过程详解 - 徽沪一郎
- Ubuntu 下的sqlmap,nmap,metasploit安装
- 判断system()执行shell脚本是否正常执行结束
- android 主题和样式-style和Theme的区别和使用
- VC++ ado连接数据库(可以在VC6.0使用,以access数据库为实例)(1)