JavaScript   发布时间:2022-04-16  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了浅谈JavaScript中小数和大整数的精度丢失大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

先来看两个问题:

0.1 + 0.2 == 0.3; // false 9999999999999999 == 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.@R_616_3890@

同理可推导出 number.min_value 的值为:

1 x 1 x 2^(-1074) = 5e-324

注意 min_value 表示最接近 0 的正数,而不是最小的数。最小的数是 -number.@R_616_3890@

小数的精度丢失

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 = 1@R_801_8899@00110011...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.@R_616_3890@,非常大的一个数。

2. JavaScript 能存储的且不丢失精度的最大整数是什么?

根据 s x m x 2^e,符号位取正,52 位尾数全填充 1,指数 e 取最大值 971,显然,答案依旧是 number.@R_616_3890@.

我们的问题究竟是什么呢?回到起始代码:

9999999999999999 == 10000000000000000; // true

很明显,16 个 9 还远远小于 308 个 10. 这个问题与 @R_616_3890@ 没什么关系,还得归属到尾数 m 只有 52 位上来。

可以用代码来描述:

var x = 1; // 为了减少运算量,初始值可以设大一点,比如 Math.pow(2,53) - 10 while(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,但目前尚未被正式采纳。

BACkground-color: #ccffcc">

最后大家:

number.@R_616_3890@ + 1 == Numer.@R_616_3890@; number.@R_616_3890@ + 2 == Numer.@R_616_3890@; ... number.@R_616_3890@ + x == Numer.@R_616_3890@; number.@R_616_3890@ + x + 1 == Infinity; ... number.@R_616_3890@ + number.@R_616_3890@ == Infinity; // 问题: // 1. x 的值是什么? // 2. Infinity - number.@R_616_3890@ == x + 1; 是 true 还是 false ?

以上这篇浅谈JavaScript中小数和大整数的精度丢失就是小编分享给大家的全部内容了,希望能给大家一个参,也希望大家多多支持菜鸟教程。

大佬总结

以上是大佬教程为你收集整理的浅谈JavaScript中小数和大整数的精度丢失全部内容,希望文章能够帮你解决浅谈JavaScript中小数和大整数的精度丢失所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。