要素の数に基づいて二分木の深さを計算するJavaScriptプログラムを作成しました。私のプログラムは何ヶ月も正常に動作していますが、最近、ChromeとFirefoxでWebページを表示したときに違いが見つかりました。
特に、Firefoxの場合:
Math.log2(8) = 3
しかし今Chromeでは:
Math.log2(8) = 2.9999999999999996
私のJavaScriptプログラムは元々、要素の数に基づいてバイナリツリーの深さを見つけるために作成されました。
var tree_depth = Math.floor(Math.log2(n_elements)) + 1;
Chromeでも正しく機能するように、この数式に簡単な変更を加えました。
var epsilon = 1.e-5;
var tree_depth = Math.floor(Math.log2(n_elements) + epsilon) + 1;
2つの質問があります:
最近Chromeの精度が変わったことに気付いた人はいますMath.log2
か?
イプシロンを追加して上記で行ったものよりもエレガントな変更はありますか?
注:
Math.log2
V8で実装されて以来、実際には変更されていません。Chromeが独自の実装を含める前に、覚えていないか、これらの特殊なケースで正しい結果が得られるシムを含めていた可能性がありMath.log2
ます。また、
Math.ceil(x)
ではなく使用する必要があるようですMath.floor(x) + 1
。
JavaScriptのさまざまな実装間での依存Math.log
やMath.log2
正確さを回避するために(使用されるアルゴリズムは実装定義です)、バイナリツリーに2 32未満の要素がある場合は、ビット演算子を使用できます。これは明らかにこれを行うための最速の方法ではありませんが(これはO(n)のみです)、比較的単純な例です。
function log2floor(x) {
// match the behaviour of Math.floor(Math.log2(x)), change it if you like
if (x === 0) return -Infinity;
for (var i = 0; i < 32; ++i) {
if (x >>> i === 1) return i;
}
}
console.log(log2floor(36) + 1); // 6
Math.log2
現在、さまざまなブラウザでどのように実装されていますか?彼らはの値の乗算に依存しているとして、Chromeの現在の実装では不正確であるMath.log(x)
ことでのMath.LOG2E
エラー(丸めにそれが影響を受けやすく、ソースを):
// ES6 draft 09-27-13, section 20.2.2.22.
function MathLog2(x) {
return MathLog(x) * 1.442695040888963407; // log2(x) = log(x)/log(2).
}
Firefoxを実行している場合は、ネイティブlog2
関数を使用するか(存在する場合)、そうでない場合(Windowsなど)はChromeと同様の実装を使用します(ソース)。
唯一の違いは、乗算する代わりに、次のように除算するlog(2)
ことです。
#if !HAVE_LOG2
double log2(double x)
{
return log(x) / M_LN2;
}
#endif
double
js::math_log2_impl(MathCache *cache, double x)
{
return cache->lookup(log2, x, MathCache::Log2);
}
double
js::math_log2_uncached(double x)
{
return log2(x);
}
bool
js::math_log2(JSContext *cx, unsigned argc, Value *vp)
{
return math_function<math_log2_impl>(cx, argc, vp);
}
他のすべてのコードは、結果をテーブルにキャッシュすることだけですが、Chromeは行いません。これはFirefoxのMath.log2
機能の精度には影響しません。
による除算Math.LN2
と乗算の違いをテストMath.LOG2E
するには、次のテストを使用できます。
function log2d(x) { return Math.log(x) / Math.LN2; }
function log2m(x) { return Math.log(x) * Math.LOG2E; }
var pow = Math.pow;
// 2^1024 rounds to Infinity
for (var i = 0; i < 1024; ++i) {
var resultD = log2d(pow(2, i));
var resultM = log2m(pow(2, i));
if (resultD !== i) console.log('log2d: expected ' + i + ', actual ' + resultD);
if (resultM !== i) console.log('log2m: expected ' + i + ', actual ' + resultM);
}
どの関数を使用しても、特定の値1に対して浮動小数点エラーが発生することに注意してください。の浮動小数点表現がlog(2)
実際の値よりも小さいため、実際の値よりも高い値になります(一方log2(e)
は低くなります)。これは、を使用log(2)
すると、これらの特殊なケースの正しい値に切り捨てられることを意味します。
1: log(pow(2, 29)) / log(2) === 29.000000000000004
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加