大量错误地在JavaScript中舍入

Jaa*_*nus 55 javascript floating-point floating-accuracy ieee-754

看到这段代码:

<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>
Run Code Online (Sandbox Code Playgroud)

当我在Firefox 3.5中看到我的控制台时,jsonParsed的值是:

Object id=714341252076979100 type=FUZZY
Run Code Online (Sandbox Code Playgroud)

即数字四舍五入.尝试了不同的值,相同的结果(数字舍入).

我也没有得到它的舍入规则.714341252076979136舍入为714341252076979200,而714341252076979135舍入为714341252076979100.

编辑:请参阅下面的第一条评论.显然这不是关于JSON,而是关于JavaScript数字处理的东西.但问题仍然存在:

为什么会这样?

Ste*_*non 62

你在这里看到的实际上是两次舍入的效果.ECMAScript中的数字在内部表示双精度浮点数.当id设置为714341252076979033(0x9e9d9958274c359十六进制)时,它实际上被赋予最接近的可表示的双精度值,即714341252076979072(0x9e9d9958274c380).当你打印出这个值时,它会被四舍五入到15个有效十进制数字14341252076979100.


T.J*_*der 52

您的JavaScript数字类型的容量超出了您的要求,详细信息请参见规范的§8.5.这些ID需要是字符串.

IEEE-754双精度浮点(JavaScript使用的数字种类)不能精确地表示所有数字(当然).众所周知,0.1 + 0.2 == 0.3是假的.这会影响整数,就像影响分数一样; 一旦你达到9,007,199,254,740,991(Number.MAX_SAFE_INTEGER)以上就会开始.

Beyond Number.MAX_SAFE_INTEGER + 1(9007199254740992),IEEE-754浮点格式不再代表每个连续的整数.9007199254740991 + 19007199254740992,但是9007199254740992 + 1 9007199254740992因为9007199254740993不能在格式来表示.接下来可能是9007199254740994.然后9007199254740995不可能,但9007199254740996可以.

原因是我们已经用完了比特,所以我们不再有1比特; 最低位现在代表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的倍数.依此类推.

  • 我喜欢这个答案,因为它实际上告诉你如何解决问题. (5认同)

tho*_*rn̈ 9

它不是由这个json解析器引起的.只需尝试输入714341252076979033到fbug的控制台.你会看到同样的714341252076979100.

有关详细信息,请参阅此博客文章:http: //www.exploringbinary.com/print-precision-of-floating-point-integers-varies-too

  • 感谢您链接到我的文章,但它只解释了一半的问题 - 内部舍入值的打印.即使JavaScript的让您打印整个事情,它仍然是错的 - 这将是最接近的可表示双精度值,如由他人解释如下. (8认同)

Chr*_*oph 5

JavaScript使用双精度浮点值,即53位的总精度,但是您需要

ceil(lb 714341252076979033) = 60
Run Code Online (Sandbox Code Playgroud)

位以精确表示该值。

可以精确表示的最接近的数字是714341252076979072(将原始数字写成二进制,将最后7位数字替换为,0并四舍五入,因为最高的替换数字为1)。

您将得到的714341252076979100不是这个数字,因为ToString()如ECMA-262所述,第9.8.1节使用10的幂,并且在53位精度下,所有这些数字都相等。