【问题标题】:Math.pow() not giving precise answerMath.pow() 没有给出准确的答案
【发布时间】:2013-12-06 22:03:07
【问题描述】:

我在我的项目中使用Math.pow() 计算指数值。

现在,对于像Math.pow(3,40) 这样的特定值,它返回12157665459056929000

但是当我使用科学计算器尝试相同的值时,它返回12157665459056928801

然后我尝试遍历循环直到指数值:

  function calculateExpo(base,power){
    base = parseInt(base);
    power = parseInt(power);
    var output = 1;
    gameObj.OutPutString = '';  //base + '^' + power + ' = ';
    for(var i=0;i<power;i++){
      output *= base;
      gameObj.OutPutString += base + ' x ';
    }
    // to remove the last comma 
    gameObj.OutPutString = gameObj.OutPutString.substring(0,gameObj.OutPutString.lastIndexOf('x'));
    gameObj.OutPutString += ' =  ' + output;
    return output;
  }

这也返回12157665459056929000。 JS中对Int类型有什么限制吗?

【问题讨论】:

  • 谁来定义scientific calculator是真实的还是math.pow?你试过第三种方法吗?如果有限制,则适用于科学计算器的结果,即较小的值
  • 这种情况下科学计算器是正确的。
  • @JosephMyers 至少这个数字是奇数,这暗示它更有可能是正确的......
  • @JosephMyers 怎么样?你能用一个论点来支持它吗?
  • 我最近阅读了一篇关于此的博客文章,但我找不到它。我能找到的最接近的东西在这里:stackoverflow.com/questions/588004/…。基本上,我认为这是因为 javascript 使用 64 位,然后最终当数字变得足够大时,它开始在更改之前连续为 2 次幂为您提供相同的数字。

标签: javascript math integer user-defined-functions


【解决方案1】:

JavaScript 只能表示 distinct 整数 to 253(或 ~16 位有效数字)。这是因为所有 JavaScript 数字都有IEEE-754 base-2 doubles 的内部表示。

因此,Math.pow 的结果(即使 在内部是准确的)被粗暴地“四舍五入”,使得结果仍然是一个 JavaScript 整数(因为它被定义为返回一个整数根据规范) - 因此,结果数字 不是 正确的值,而是 JavaScript 可以处理的最接近的整数近似值。

我在不[完全]构成“有效数字”截止的数字上方加了下划线,以便了解这将如何影响结果。

................____
12157665459056928801     - correct value
12157665459056929000     - closest JavaScript integer

另一种查看方法是运行以下命令(结果为真):

12157665459056928801 == 12157665459056929000

来自规范中的The Number Type 部分:

注意所有大小不大于253的正负整数都可以用Number类型表示..

.. 但并不是所有大的整数都是可表示的。


在 JavaScript 中处理这种情况(这样信息不会丢失)的唯一方法是使用 external 数字编码和 pow 函数。 https://stackoverflow.com/questions/287744/good-open-source-javascript-math-library-for-floating-point-operationsIs there a decimal math library for JavaScript? 中提到了几个不同的选项

例如,使用big.js,代码可能看起来像this fiddle

var z = new Big(3)
var r = z.pow(40)
var str = r.toString()
// str === "12157665459056928801"

【讨论】:

  • 哇,有多少编辑?大声笑,我一开始阅读它就说已经进行了编辑:D。哦,你又来了
  • @Hanky웃Panky 是的,我需要去 EAA 课程 :(
  • 那有什么解决办法呢?我该怎么办?
  • @janakshah 我已经使用 big.js 添加了一个解决方案,尽管还有其他可用的库。无论做什么,pow(3, 40) 的结果不能是单个 JavaScript 数字。
【解决方案2】:

JavaScript 的 64 位双精度数值的 52 位用于存储数字的“分数”部分(执行计算的主要部分),而 11 位用于存储“指数”(基本上,小数点的位置),第 64 位用于符号。 (更新:见此图:http://en.wikipedia.org/wiki/File:IEEE_754_Double_Floating_Point_Format.svg

3^40 的基数为 2 的扩展中的有效数字略高于 63 位(连续意义上的 63.3985...,离散意义上的 64),因此无法使用精确计算Math.pow(3, 40) 在 JavaScript 中。只有在以 2 为底的扩展中有效数字为 52 或更少的数字(以及对它们在 11 位内拟合的数量级的类似限制)才有机会用双精度浮点值准确表示。

请注意,数字有多大并不重要,重要的是使用多少有效数字来表示以二为底的数字。有许多数字与3^40 一样大或更大,可以用 JavaScript 的 64 位双精度数值准确表示。

注意:

3^40 = 1010100010111000101101000101001000101001000111111110100000100001(基数二)

(以 1 开头和结尾的最大子字符串的长度是基数为 2 的有效数字的个数,在这种情况下是 64 位的整个字符串。)

【讨论】:

    【解决方案3】:

    Haskell (ghci) 给出

    Prelude> 3^40
    12157665459056928801
    

    Erlang 给出

    1> io:format("~f~n", [math:pow(3,40)]).
    12157665459056929000.000000
    2> io:format("~p~n", [crypto:mod_exp(3,40,trunc(math:pow(10,21)))]).
    12157665459056928801
    

    JavaScript

    > Math.pow(3,40)
      12157665459056929000
    

    您会得到12157665459056929000,因为它使用 IEEE 浮点进行计算。你会得到12157665459056928801,因为它使用任意精度(bignum)进行计算。

    【讨论】:

      【解决方案4】:

      此行为高度依赖于您运行此代码的平台。有趣的是,即使在同一台机器上,浏览器也很重要。

      <script>
      document.write(Math.pow(3,40));
      </script>
      

      在我的 64 位机器上结果如下:

      IE1112157665459056928000

      FF2512157665459056929000

      CH3112157665459056929000

      SAFARI: 12157665459056929000

      【讨论】:

      • IE11 降了 1000 真的很奇怪。我认为它应该保持一致。
      • 你可以在你的机器上试一下,看看有没有不同?
      • 我没有IE11,但是IE10/Win7/x64也报“12157665459056928000”。
      • 我对此投了赞成票,因为我认为 pow 函数的行为不同 - 至少在极限情况下,这很奇怪。我没想到会有这种行为,即使我确实预料到会丢失一些信息。
      • 12157665459056928000 === 12157665459056929000(IE11 刚刚决定将其转换为不同的字符串,但它们都是相同的数字。不足为奇,因为它们都可能使用 C 运行时中的pow
      【解决方案5】:

      不能说我肯定知道,但这看起来确实是范围问题。

      我相信数学库通常使用对数来实现求幂。这要求将两个值都转换为浮点数,因此结果在技术上也是浮点数。当我要求 MySQL 进行相同的计算时,这是最能说明问题的:

      > select pow(3, 40);
      +-----------------------+
      | pow(3, 40)            |
      +-----------------------+
      | 1.2157665459056929e19 |
      +-----------------------+
      

      您实际上得到了一个大整数,这可能是一种礼貌。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-23
        • 2021-10-25
        • 1970-01-01
        • 1970-01-01
        • 2019-03-07
        • 2019-10-28
        相关资源
        最近更新 更多