为了账号安全,请及时绑定邮箱和手机立即绑定

用JavaScript错误地舍入大数

用JavaScript错误地舍入大数

www说 2019-06-14 10:43:12
用JavaScript错误地舍入大数请参阅以下代码:<html>   <head>      <script src="http://www.json.org/json2.js" type="text/javascript"></script>     <script type="text/javascript">       var jsonString = '{"id":714341252076979033,"type":"FUZZY"}';       var jsonParsed = JSON.parse(jsonString);       console.log(jsonString, jsonParsed);     </script>   </head>   <body>   </body></html>当我在Firefox 3.5中看到我的控制台时,jsonParsed的值是:Object id=714341252076979100 type=FUZZY即数字是四舍五入的。尝试不同的值,结果相同(数字四舍五入)。我也不知道它的舍入规则。714341252076979136四舍五入为714341252076979200,而714341252076979135四舍五入为714341252076979100。编辑:见下面的第一个评论。显然,这不是关于JSON的,而是关于JavaScript数字处理的。但问题仍然存在:为什么会发生这种事?
查看完整描述

3 回答

?
慕码人8056858

TA贡献1803条经验 获得超6个赞

你在这里看到的实际上是两个圆的效果。ECMAScript中的数字在内部表示为双精度浮点。什么时候id设置为714341252076979033 (0x9e9d9958274c359在十六进制中,它实际上被分配给最近可表示的双精度值,即714341252076979072 (0x9e9d9958274c380)。当打印出该值时,它被舍入为15个有效小数位数,这将给出14341252076979100.


查看完整回答
反对 回复 2019-06-14
?
慕丝7291255

TA贡献1859条经验 获得超6个赞

JavaScript的数字类型的容量超过了,请参见规范第8.5条关于细节。这些ID需要是字符串。

ieee-754双精度浮点(JavaScript使用的那种数字)不能精确地表示。数字(当然)。著名的是,0.1 + 0.2 == 0.3都是假的。这可以影响整数,就像它影响小数一样;当你超过9,007,199,254,740,991时,它就开始了(Number.MAX_SAFE_INTEGER).

超越Number.MAX_SAFE_INTEGER + 1 (9007199254740992),IEEE-754浮点格式不能再表示每个连续整数。9007199254740991 + 19007199254740992,但是9007199254740992 + 1 9007199254740992因为9007199254740993无法以格式表示。下一个可以是9007199254740994..然后9007199254740995不可能,但是9007199254740996能,会,可以。

原因是我们已经没有比特,所以我们不再有1s位;最低阶位现在表示2的倍数。最后,如果我们继续下去,我们就失去了这一点,并且只在4的倍数中工作。诸若此类。

你的价值观是超过这个阈值,它们被四舍五入到最近的可表示值。


如果你对比特感兴趣,下面是发生的事情:ieee-754二进制双精度浮点数字有一个符号位,11位指数(它定义了该数字的总比例尺为2(因为这是一种二进制格式),还有52位的重要意义(但这种格式非常聪明,从这52位中获得了53位的精度)。如何使用指数很复杂(在此描述),但在非常模糊项,如果我们在指数中加一个,则意义的值是加倍的,因为指数用于2的幂(同样,这里的警告,它不是直接的,那里有聪明)。

所以让我们看看这个值9007199254740991(又名:Number.MAX_SAFE_INTEGER):

   +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit
  / +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent
 / /        |  +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand
/ /         | /                                                  |
0 10000110011 1111111111111111111111111111111111111111111111111111
                = 9007199254740991 (Number.MAX_SAFE_INTEGER)

指数值,10000110011,这意味着,每次我们在意义上添加一个,所表示的数字就会上升1(整个数字1,我们就失去了表示小数的能力)。

但现在意义已经满了。要超过这个数字,我们必须增加指数,这意味着,如果我们在意义上加一个,所表示的数字的值就会上升2,而不是1(因为指数被应用于2,这个二进制浮点数的基):

   +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit
  / +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent
 / /        |  +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand
/ /         | /                                                  |
0 10000110100 0000000000000000000000000000000000000000000000000000
                = 9007199254740992 (Number.MAX_SAFE_INTEGER + 1)

好吧,没关系,因为9007199254740991 + 19007199254740992不管怎么说。但!我们不能代表9007199254740993..我们的零碎已经用完了。如果我们仅将1添加到这个值中,它就会将2添加到以下值:

   +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit
  / +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent
 / /        |  +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand
/ /         | /                                                  |
0 10000110100 0000000000000000000000000000000000000000000000000001
                = 9007199254740994 (Number.MAX_SAFE_INTEGER + 3)

这种格式不能再表示奇数,因为我们增加了数值,指数太大了。

最后,我们又没有意义位,必须增加指数,所以我们只能表示4的倍数。然后是8的倍数。然后乘以16。诸若此类。


查看完整回答
反对 回复 2019-06-14
  • 3 回答
  • 0 关注
  • 336 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号