つまり、手短に言えば、
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。
これに対するグーグルでのトップヒットは数学博士によるもので、説明はかなり良いです。実数(とにかく複素数ではない)の場合、分数乗した負の数は実数ではない可能性があります。最も単純な例はおそらく
-4 ^ (1/2)
これは基本的に -4 の平方根を計算しています。-8 の 3 乗根には実数の解がありますが、ほとんどのソフトウェア ライブラリは、すべての複雑な算術演算を実行せず、虚数部が非ゼロの場合にのみ NaN を返し、それ以外の場合は適切な実数の答えを返す方が効率的だと思います。
編集
NaN
これが意図した結果であることを明確にするために、公式の ECMAScript 5.1 仕様のセクション 15.8.2.13 を参照してください。それは言います:
x<0 で x が有限で y が有限で y が整数でない場合、結果は NaN になります。
繰り返しになりますが、負の数を分数に累乗するいくつかの例では、実根が 1 つしかありませんが、多くの言語は、負の数を分数の根にするすべてのケースに対して NaN 処理を行うだけです。
JavaScript だけがそのような言語だとは思わないでください。 C++ は同じことを行います:
x が有限の負であり、y が有限であるが整数値ではない場合、定義域エラーが発生します。
2 つの重要な問題:
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);
};
いくつかのテストケースをいじって、バグを見つけたら私に一言お願いします!
注意点として、ES6 には Math.cbrt 関数が追加されました。
Google chrome でのテストでは、Math.pow のほぼ 2 倍の速さで動作するようです。興味深いことに、結果を合計する必要がありました。それ以外の場合、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
*/