计算负数的立方根

时间:2012-10-10 01:38:33

标签: javascript math

所以,简而言之,

  

3 √(-8)=( - 8) 1/3

console.log(Math.pow(-8,1/3));
//Should be -2

但是当我测试它时,它会输出

NaN

为什么呢?这是一个错误还是预计会首先出现这种情况?我使用JavaScript绘制图形,但这会弄乱图形。

10 个答案:

答案 0 :(得分:17)

您可以使用此代码段进行计算。它也适用于其他权力,例如1/41/5

function nthroot(x, n) {
  try {
    var negate = n % 2 == 1 && x < 0;
    if(negate)
      x = -x;
    var possible = Math.pow(x, 1 / n);
    n = Math.pow(possible, n);
    if(Math.abs(x - n) < 1 && (x > 0 == n > 0))
      return negate ? -possible : possible;
  } catch(e){}
}

nthroot(-8, 3);

来源:http://gotochriswest.com/blog/2011/05/06/cube-root-an-beyond/

计算立方根的更快方法:

Math.cbrt = function(x) {
    var sign = x === 0 ? 0 : x > 0 ? 1 : -1;

    return sign * Math.pow(Math.abs(x), 1 / 3);
}

Math.cbrt(-8);

<强>更新

要查找基于整数的立方根,您可以使用受this answer启发的以下函数:

// positive-only cubic root approximation
function cbrt(n)
{
    var a = n; // note: this is a non optimized assumption

    while (a * a * a > n) {
        a = Math.floor((2 * a + (n / (a * a))) / 3);
    }

    return a;
}

首先假设收敛到a的最接近的整数a^3 <= n。可以用相同的方式调整此功能以支持负底座。

答案 1 :(得分:11)

没有错误;你把负数提高到一个分数幂;因此,NaN。

谷歌的最高点来自Dr Math,解释非常好。它表示对于实数(无论如何不是复数),提升到分数幂的负数可能不是实数。最简单的例子可能是

-4 ^(1/2)

基本上计算-4的平方根。尽管-8的立方根确实有真正的解决方案,但我认为大多数软件库都发现不进行所有复杂算术更有效,只有当虚部非零时才返回NaN,否则会给你很好的答案。 / p>

修改

只是为了清楚地表明NaN是预期的结果,see the official ECMAScript 5.1 Specification, Section 15.8.2.13。它说:

  

如果x <0且x是有限的且y是有限的且y不是整数,则结果为NaN。

同样,即使将负数提升到分数幂的一些实例只有一个真正的根,但是对于所有负数到分数根的情况,许多语言只做NaN事件。

请不要认为JavaScript是唯一这样的语言。 C++ does the same thing

  

如果x是有限负的且y是有限的但不是整数值,则会导致域错误。

答案 2 :(得分:7)

两个关键问题:

  1. 在数学上,有一个负数的立方根:-2,但也有两个复根(见cube roots of unity)。
  2. Javascript的Math对象(以及大多数其他标准数学库)不会执行负数的分数幂。它在函数接收之前将分数幂转换为浮点数,因此您要求函数计算负数的浮点幂,这可能有也可能没有真正的解。所以它做了务实的事情并且拒绝尝试计算这样的价值。
  3. 如果你想得到正确的答案,你需要决定你想要的数学校正,并将这些规则写入pow的非标准实现中。

    所有库函数都受到限制,以避免过多的计算时间和不必要的复杂性。

答案 3 :(得分:5)

我喜欢其他答案,但如果覆盖Math.pow,那么它就可以使用所有第n个负数根:

//keep the original method for proxying
Math.pow_ = Math.pow;

//redefine the method
Math.pow = function(_base, _exponent) {
  if (_base < 0) {
    if (Math.abs(_exponent) < 1) {
      //we're calculating nth root of _base, where n === 1/_exponent
      if (1 / _exponent % 2 === 0) {
        //nth root of a negative number is imaginary when n is even, we could return
        //a string like "123i" but this would completely mess up further computation
        return NaN;
      }/*else if (1 / _exponent % 2 !== 0)*/
      //nth root of a negative number when n is odd
      return -Math.pow_(Math.abs(_base), _exponent);
    }
  }/*else if (_base >=0)*/
  //run the original method, nothing will go wrong
  return Math.pow_(_base, _exponent);
};

Fiddled有一些测试用例,如果发现错误,请给我一个喊叫声!

答案 4 :(得分:4)

所以我看到一堆围绕Math.pow(...)的方法很酷,但基于赏金的措辞,我提出的方法略有不同。

有几种计算近似解决根,有些采取比其他更快的步骤。最终停止点达到所需的精度(这取决于你/正在解决的问题)。

我不打算详细解释数学,但是以下是通过目标测试的立方根近似的实现(赏金测试 - 也增加了负范围,因为这个问题标题)。循环中的每次迭代(参见每个方法中的while(Math.abs(xi-xi0)>precision)循环)都会更接近所需的精度。一旦达到精度,就会对数字应用格式,因此它与从迭代中得到的计算一样精确。

var precision = 0.0000000000001;
function test_cuberoot_fn(fn) { 
    var tested = 0, 
    failed = 0; 
    for (var i = -100; i < 100; i++) { 
        var root = fn(i*i*i); 
        if (i !== root) { 
            console.log(i, root); 
            failed++; 
        } 
        tested++; 
    } 
    if (failed) { 
        console.log("failed %d / %d", failed, tested); 
    }else{
        console.log("Passed test");
    }
}
test_cuberoot_fn(newtonMethod);
test_cuberoot_fn(halleysMethod);

牛顿逼近实施

function newtonMethod(cube){
    if(cube == 0){//only John Skeet and Chuck Norris
        return 0; //can divide by zero, we'll have
    }             //to settle for check and return
    var xi = 1;
    var xi0 = -1;
    while(Math.abs(xi-xi0)>precision){//precision = 0.0000000000001
        xi0=xi;
        xi = (1/3)*((cube/(xi*xi))+2*xi);
    }
    return Number(xi.toPrecision(12));
}

Halley的近似实现 note Halley的近似可以更快地完成求解立方体的步骤,因此它的计算速度比牛顿近似要快。

function halleysMethod(cube){
    if(cube == 0){//only John Skeet and Chuck Norris
        return 0; //can divide by zero, we'll have
    }             //to settle for check and return
    var xi = 1;
    var xi0 = -1;
    while(Math.abs(xi-xi0)>precision){//precision = 0.0000000000001
        xi0=xi;
        xi = xi*((xi*xi*xi + 2*cube)/(2*xi*xi*xi+cube));
    }
    return Number(xi.toPrecision(12));
}

答案 5 :(得分:3)

它在Chrome控制台中工作

function cubeRoot(number) {
    var num = number;
    var temp = 1;
    var inverse = 1 / 3;
    if (num < 0) {
        num = -num;
        temp = -1;
    }
    var res = Math.pow(num, inverse);
    var acc = res - Math.floor(res);
    if (acc <= 0.00001)
        res = Math.floor(res);
    else if (acc >= 0.99999)
        res = Math.ceil(res);
    return (temp * res);
}

cubeRoot(-64) // -4
cubeRoot(64)  //  4

答案 6 :(得分:1)

//除了符号外,负数的立方根不是正数吗?

Math.cubeRoot= function(n, r){
    var sign= (n<0)? -1: 1;
    return sign*Math.pow(Math.abs(n), 1/3);
}
Math.cubeRoot(-8)

/*  returned value: (Number)
-2
*/

答案 7 :(得分:1)

首先,在ES6中,现在有一个Math.cbrt函数。

在我的Google Chrome测试中,它的工作速度几乎是Math.pow的两倍。有趣的是,我不得不将结果加起来,否则chrome在优化掉功能方面做得更好。

//do a performance test on the cube root function from es6
var start=0, end=0, k=0;
start = performance.now();
k=0;
for (var i=0.0; i<10000000.0; i+=1.0)
{
    var j = Math.cbrt(i);
    //k+=j;
}
end = performance.now();
console.log("cbrt took:" + (end-start),k);
k=0;
start = performance.now();
for (var i=0.0; i<10000000.0; i+=1.0)
{
    var j = Math.pow(i,0.33333333);
    //k+=j;
}
end = performance.now();
console.log("pow took:" + (end-start),k);
k=0;
start = performance.now();
for (var i=0.0; i<10000000.0; i+=1.0)
{
    var j = Math.cbrt(i);
    k+=j;
}
end = performance.now();
console.log("cbrt took:" + (end-start),k);
k=0;
start = performance.now();
for (var i=0.0; i<10000000.0; i+=1.0)
{
    var j = Math.pow(i,0.33333333);
    k+=j;
}
end = performance.now();
console.log("pow took:" + (end-start),k);

结果:

cbrt took:468.28200000163633 0
pow took:77.21999999921536 0
cbrt took:546.8039999977918 1615825909.5248165
pow took:869.1149999940535 1615825826.7510242

答案 8 :(得分:1)

只想强调在ES6中有一个native cubic root功能。所以你可以这样做(查看支持here

Math.cbrt(-8)会返回-2

答案 9 :(得分:0)

这适用于负数和负指数:

function nthRoot(x = 0, r = 1) {
  if (x < 0) {
    if (r % 2 === 1) return -nthRoot(-x, r)
    if (r % 2 === -1) return -1 / nthRoot(-x, -r)
  }
  return x ** (1 / r)
}

示例:

nthRoot( 16,  2)   4
nthRoot( 16, -2)   0.25
nthRoot(-16,  2)   NaN
nthRoot(-16, -2)   NaN
nthRoot( 27,  3)   3
nthRoot( 27, -3)   0.3333333333333333
nthRoot(-27,  3)   -3
nthRoot(-27, -3)   -0.3333333333333333