Math.pow
的ECMAScript规范有以下特殊规则:
- 如果 x < 0 and x is finite and y is finite and y is not an integer, the result is NaN.
(http://es5.github.com/#x15.8.2.13)
结果
Math.pow(-8, 1 / 3)
给出NaN
而不是-2
这条规则的原因是什么?这个规则是否有某种更广泛的计算机科学或 IEEEish 原因,或者它只是 TC39/Eich 从前做出的选择?
感谢 Amadan 与我的交流,我想我现在明白了其中的道理。为了后代,我想扩大我们的讨论。
让我们来看下面的例子:
Math.pow(823543, 1 / 7)
产生 6.999999999999999
尽管它确实应该是 7
。这是由于 1 / 7
必须首先转换为十进制表示形式 0.14285714285714285
而引入的不准确性,它被截断并失去精度。当我们处理正数时,这不是一个糟糕的问题,因为我们仍然得到非常接近真实结果的结果。
然而,一旦我们踏入负面世界,我们就会遇到问题。如果 JavaScript 引擎要尝试计算
Math.pow(-823543, 1 / 7)
,它首先需要将 1 / 7
转换为小数,所以它实际上是在计算 Math.pow(-823543, 0.14285714285714285)
,实际上 没有真正的答案。在这种情况下,它可能不得不返回NaN
,因为它找不到实数,即使真正的答案应该是-7
。此外,寻找接近实数的复数以做出“最佳猜测”可能涉及一定程度的复杂性,他们不希望在数学领域需要 JS 引擎。
我的猜测是由于考虑到浮点数的精度损失,导致他们制定了负数对非整数幂的规则应始终为
NaN
——基本上是因为非整数幂是可能由于精度损失而给出一个复数,即使它不应该,并且可能没有好的方法从中恢复。
对此,我相当满意,但我确实欢迎进一步的信息。
我假设是因为那些情况导致结果进入复杂的水域,而 ECMAScript 没有配备虚数。具体来说,您的示例应该产生接近
1 + 1.732i
的结果,以及其他结果。 (事实上 -2 也是一个可能的结果是无关紧要的 - 这是一个意外而不是一个规则。)
您可以使用辅助函数。
很快我就遇到了类似的情况。这是为您建议的解决方案:
function checkPower(x, y) {
if (x > 0 || Math.round(y) == y) {
return Math.pow(x, y)
} else {
var r = -1 * Math.pow(-x, y);
if( Math.pow( r, 1/y) == x)
return r;
else
return NaN;
}
}