web-dev-qa-db-ja.com

ダブルをすばやく二乗

ダブル(_double d_)を二乗する最速の方法を探しています。これまでのところ、2つのアプローチを思いつきました。

_1. d*d
2. Math.pow(d, 2)
_

パフォーマンスをテストするために、3つのテストケースを設定し、それぞれで3つのケースに同じシードを使用して乱数を生成し、ループで2乗数を100 000 000回計算します。

最初のテストケースではrandom.nextDouble()を使用して番号が生成され、2番目のケースではrandom.nextDouble()*Double.MAX_VALUEを使用して3番目のテストケースではrandom.nextDouble()*Double.MIN_VALUEを使用します。

数回の実行の結果(概算の結果、常に多少のばらつきがあり、Java 1.8、Mac OSX MavericksでコンパイルされたJava 1.6)を使用して実行)

_Approach | Case 1 | Case 2 | Case 3
---------•--------•--------•-------
    1    | ~2.16s | ~2.16s | ~2.16s
    2    | ~9s    | ~30s   | ~60s
_

結論は、アプローチ1の方がはるかに高速であるが、_Math.pow_は一種の奇妙な振る舞いをしているようだと思われます。

そこで、2つの質問があります。

1なぜ_Math.pow_が非常に遅いのか、そしてなぜ_> 1_にひどく対処し、さらに_< -1_数字にさらに悪いのでしょうか?

2アプローチ1として提案したものよりもパフォーマンスを改善する方法はありますか?私は次のようなことを考えていました:

_long l = Double.doubleToRawLongBits(d);
long sign = (l & (1 << 63));
Double.longBitsToDouble((l<<1)&sign);
_

しかし、それはa)間違っており、b)アプローチ1とほぼ同じ速度です。

17
Samuel

数値を2乗する最も速い方法は、それ自体を乗算することです。

なぜMath.pow とても遅いです?

実際にはそうではありませんが、単純な乗算の代わりに exponentiation を実行しています。

そして、なぜそれは> 1の数でひどく対処し、さらに-1未満の数でさらに悪化しますか

最初に、それは数学をするからです。 Javadoc からは、多くのコーナーケースのテストも含まれています。最後に、私はあなたのマイクロベンチマークにあまり依存しません。

10
Elliott Frisch

Selfとの乗算による二乗は最速です。そのアプローチは、単純な非分岐バイトコード(したがって、間接的にマシンコード)に直接変換できるためです。

Math.pow()は非常に複雑な関数であり、Edgeの場合にさまざまな保証が付いています。そして、インライン化される代わりに呼び出される必要があります。

6
Durandal

Math.pow()は、一般的なケースを処理したり、数値を任意の累乗に上げたりする必要があるため、低速です。
負の数で遅い理由は、符号を付けるためにパワーが正か負かをテストする必要があるためです。

2
Varpie