所以,简而言之,
3 √(-8) = (-8) 1/3
console.log(Math.pow(-8,1/3));
//Should be -2
但是当我测试它时,它会输出
NaN
为什么?它是一个错误还是一开始就应该是这样的?我正在使用 JavaScript 来绘制图表,但这会弄乱图表。
所以,简而言之,
3 √(-8) = (-8) 1/3
console.log(Math.pow(-8,1/3));
//Should be -2
但是当我测试它时,它会输出
NaN
为什么?它是一个错误还是一开始就应该是这样的?我正在使用 JavaScript 来绘制图表,但这会弄乱图表。
您可以使用此代码段来计算它。它也适用于其他权力,例如1/4
,1/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);
更新
要查找基于整数的三次根,您可以使用以下函数,受此答案的启发:
// 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
。可以以相同的方式调整此功能以支持负基数。
没有错误;您将负数提高到分数幂;因此,NaN。
谷歌上最热门的是来自Dr Math的解释非常好。它说对于实数(无论如何都不是复数),将负数提高到分数幂可能不是实数。最简单的例子大概是
-4 ^ (1/2)
这本质上是计算-4的平方根。尽管 -8 的三次根确实有真正的解决方案,但我认为大多数软件库发现不执行所有复杂算术并且仅在虚部非零时才返回 NaN 更有效,否则会给您很好的真实答案。
编辑
为了明确说明这NaN
是预期的结果,请参阅官方的 ECMAScript 5.1 规范,第 15.8.2.13 节。它说:
如果 x<0 且 x 是有限的且 y 是有限的且 y 不是整数,则结果为 NaN。
同样,即使将负数提高到分数次方的某些实例只有一个实根,但许多语言只是对所有负数到分数根的情况进行 NaN 操作。
请不要认为 JavaScript 是唯一这样的语言。 C++ 做同样的事情:
如果 x 是有限负并且 y 是有限但不是整数值,则会导致域错误。
两个关键问题:
Math
对象(和大多数其他标准数学库)不会做负数的分数幂。它在函数接收它之前将小数幂转换为浮点数,因此您要求函数计算负数的浮点幂,这可能有也可能没有真正的解决方案。所以它做了务实的事情,拒绝尝试计算这样的值。如果你想得到正确的答案,你需要决定你想要在数学上有多正确,并将这些规则写入pow
.
所有库函数都受到限制,以避免过多的计算时间和不必要的复杂性。
我喜欢其他答案,但是如何覆盖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);
};
摆弄了一些测试用例,如果您发现错误,请大声疾呼!
所以我看到了一堆围绕着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's approximation 实施 说明Halley's approximation 采用更快的步骤来求解立方体,因此它的计算速度比 newton's 逼近。
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));
}
它在 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
作为提醒,在 ES6 中现在有一个 Math.cbrt 函数。
在我在 Google chrome 中进行的测试中,它的运行速度几乎是 Math.pow 的两倍。有趣的是,我不得不将结果相加,否则 chrome 在优化 pow 函数方面做得更好。
//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
//除了符号之外,负数的立方根不与正数相同吗?
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
*/
这适用于负数和负指数:
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