javascript
  • floating-point
  • floating-accuracy
  • ieee-754
  • 2009-09-04 20 views 38 likes 
    38

    错误四舍五入大数看到这个代码:在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数字处理的一些问题。但问题仍然存在:

    这是为什么发生?

    +0

    感谢所有的快速有益的答案,我希望我可以标记所有3作为正式答案。 – Jaanus

    回答

    47

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

    7

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

    看到这个博客帖子的细节: http://www.exploringbinary.com/print-precision-of-floating-point-integers-varies-too

    +6

    感谢链接到我的文章,但它只解释了一半问题 - 内部舍入值的打印。即使javascript让你打印整个东西,它仍然是错误的 - 它将是最接近的可表示的双精度值,正如下面的其他人所解释的。 –

    40

    你满溢JavaScript的号码类型的能力,看到§8.5 of the spec了解详情。这些ID将需要字符串。

    IEEE-754双精度浮点数(JavaScript使用的数字种类)不能精确地表示全部数字(当然)。着名的,0.1 + 0.2 == 0.3是错误的。这可能影响整数,就像影响分数一样;一旦您获得9,007,199,254,740,991(Number.MAX_SAFE_INTEGER)以上,它就会启动。

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

    原因是我们已经用尽了位,所以我们不再有1位;最低位现在代表2的倍数。最终,如果我们继续前进,我们会失去那一点,只能以4的倍数工作。依此类推。

    您的值是以及高于该阈值,因此它们会四舍五入到最接近的可表示值。


    如果你好奇的位,这里发生的事情:一个IEEE-754二进制双精度浮点数有一个符号位,指数为11位(定义数的整体规模作为2的幂[因为这是一种二进制格式])和52位有效位(但格式非常聪明,它在52位中得到53位精度)。如何使用指数是复杂的(described here),但在非常模糊的术语,如果我们加上一个指数,有效数字的值加倍,因为指数用于2的幂(同样,在那里注意,这不是直接的,那里有聪明)。

    那么让我们来看看价值9007199254740991(又名Number.MAX_SAFE_INTEGER):

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

    也就是说指数值,10000110011,意味着我们每次添加一个到尾数时间,代表人数由1上升(中整数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的倍数。等等。

    +4

    我喜欢这个答案,因为它实际上告诉你如何解决这个问题。 – jsh

    2

    问题是你的数字需要比JavaScript更高的精度。

    您可以将该号码作为字符串发送吗?分为两部分?

    4

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

    ceil(lb 714341252076979033) = 60 
    

    位准确地表示值。

    最接近的可精确表示的数字是714341252076979072(用二进制编写原始数字,用0替换最后的7位数字,因为最高的替换数字是1)。

    你会得到714341252076979100而不是这个数字,因为ToString()如ECMA-262§9.8.1所描述的那样使用10的幂和53位的精度,所有这些数字都是相等的。

    1

    JavaScript只能处理高达约9000万的确切整数(即9个有15个零)。比这更高,你会得到垃圾。通过使用字符串来解决这个问题。如果你需要用这些数字进行数学计算,请编写自己的函数,或者看看能否为他们找到一个图书馆:我建议前者,因为我不喜欢我见过的图书馆。为了让你开始,请参阅another answer的两项功能。

    相关问题