私がこの質問をする方法で明らかになるであろう素朴さ、そして私がそれを求めているという事実を許してください。
数学者は、通常、を使用します(理論により)理論上最も単純な/最も基本的なものです。しかし、コンピューターはすべてをバイナリで実行するように見えるので、コンピューター上で計算する2**x
よりも高速Math::exp(x)
ですか?
私がこの質問をする方法で明らかになるであろう素朴さ、そして私がそれを求めているという事実を許してください。
数学者は、通常、を使用します(理論により)理論上最も単純な/最も基本的なものです。しかし、コンピューターはすべてをバイナリで実行するように見えるので、コンピューター上で計算する2**x
よりも高速Math::exp(x)
ですか?
回答:
これはStackoverflowではなくCSであるため、数値解析、および(物事を簡単にするために)IEEE-754浮動小数点について質問していると想定します。その場合、あなたの質問への答えは、「より簡単」という意味と、システムの詳細に一部依存します。
私が知っている最新のCPUには、操作(以降、Cの通常の名前と呼ぶ)または2 x()のどちらかで、期待どおりの動作を行う命令が組み込まれていません。どちらもライブラリ関数を使用して実装されます。exp
exp2
超越演算のすべての数値メソッドの場合と同様に、考慮すべき特別なケースがいくつかあります。
exp(NaN) = NaN
exp(+Inf) = +Inf
exp(-Inf) = 0
ただし、問題をもう少し複雑にする別のことがあります:有用なドメインは非常に小さいです。binary32のexp(x)
場合、程度であればアンダーフローし、x > 88.7程度であればオーバーフローします。以来、珍しく超越操作のために、我々はまた、非正規のケースを無視することができると区別がつかない場合は正常以下です。ドメインがわずかに異なることを除いて、上記のすべてもに当てはまります。exp(x)
1.0
x
exp2
ほとんどの実装が計算するという点で、あなたの直感は正しいです。ただし、その乗算のコストは1は、他のコンピューティングと比べると些細なことです。典型的な方法は、K個の要素を持つ事前計算されたテーブルを使用します。exp2
ここで、はxの整数部であり、テーブルTは、範囲[ 0 、K )のすべてのjに対して2 j / Kの値を含み、Pは、範囲内の2 x(binary32には4次で十分)の多項式近似です[ 0 、1。2nはそれだけで指数を操作していますので、一部には、安いです。Tはルックアップテーブルです。したがって、Pは操作のコストの高い部分になる可能性があります。
私は、Intelのx86のFPUことを完全にするために指摘すべきであると呼ばれる命令含むf2xm1
計算し、ためのxの範囲で、[ - 1 、1 ]。ただし、最新のCPUでは、これはかなり高価でパイプライン化されていない命令であり、使用することは非常に推奨されません。インテル最適化リファレンスマニュアルのセクション3.8.5当然の注意事項:
x87は超越命令をサポートしていますが、多くの場合、超越関数のソフトウェアライブラリ実装はより高速です。
編集: IEEE 754-2008で使用されている新しい用語のいくつかを説明する必要があるというコメントで指摘されています。言語の一部は1985年と1987年以降に変更されており、ほとんどの人は古い専門用語にはるかに精通しています。
「binary32」および「binary64」という用語は、32ビットおよび64ビットの2進浮動小数点数の新しい名前であり、古い標準ではそれぞれ「single」および「double」と呼ばれていました。
用語「非正規数」は、前の用語「非正規数」または「非正規化数」を置き換えます。
もし2 x2**x
を意味するなら、はい。左シフト演算子を使用できます。つまり、を計算できます。これは私が知っているすべてのプロセッサでの原始的な機械命令であるため、非常に高速です。これは、2以外の基数では実行できません。さらに、浮動小数点数の乗算に時間がかかるため、整数のべき乗は常に実際のべき乗よりも高速になります。<<
1 << x
x
が整数でない場合(たとえば、20.75
)、仮数部2
と指数部を丸めた値にx
最も正確な推定値として設定します(正確な表現は不可能です)。これも、「パウ」よりもはるかに高速です。
2 ^ x = e ^(x * ln 2)およびe ^ x = 2 ^(x * log2(e))なので、大きな違いは期待できないでしょう。
xがゼロに近い場合は、通常、多項式e ^ x = 1 + x + x ^ 2/2 + x ^ 3/6 ...を使用します。 。明らかに、2 ^ xは非常に小さく、計算が少し遅くなります。「0に近いx」は通常、sqrt(1/2)<= e ^ x <= sqrt(2)のxの値になります。xの範囲を制限することにより、多項式の次数をあまり高く選択する必要がなくなります。
より大きなxの場合、通常はx = x '+ x' 'として2 ^ xを計算します。ここで、x'は整数で、-0.5 <= x '' <= 0.5です。2 ^ x 'は、正しいビットパターンで浮動小数点数を構成することによって計算され、2 ^ x' 'は小さなxに対してe ^ xメソッドを使用して計算されます。ここで、2 ^ xはほんの少し高速です。さらに、xが大きければ(たとえば、x = 100.3)、xにlog2(e)を掛けると、許容できない丸め誤差が発生し(小数ビットが少ないため)、さらに注意する必要があります。
そして、うまくいけば、優れたライブラリ関数が、丸め誤差が何であろうと、x <= y、e ^ x <= e ^ yおよび2 ^ x <= 2 ^ yであることに注意してください。そのようなことを達成するのは難しい場合があります。
コンピューターでの計算は、ソフトウェアごとに異なる方法で行われ、一貫した答えが得られることを理解する必要があります。ほとんどのソフトウェアを見ると、コンピューターはうまく振る舞うと思います。コンピューターは、0 ^ 0のような場合でも、長い道のりで答えを計算します。問題は、特殊なケースには「認識」が関係することであり、これはデジタルコンピューターでは無料では発生しません。これは、答えが「最も」速くなる場合にのみ最適化が発生することを意味します。しかし、それらの場合、それは非常によく起こります。また、正しい答えを得るには、いくつかの異なる認識が必要になる場合があることに注意してください。これは速度最適化レベルと呼ばれ、GNU「C」と呼ばれるほとんどのソフトウェアに基づいて、最大の専門的範囲で発生しました。これは、ここではソフトウェアからソフトウェアへの実行時間のわずかな違いと、マシンからマシンへの品質の差が品質の許容値として使用されるためです。他のインタープリターでは、通常、以前の計算の副作用として「ゼロフラグ」が発生した場合にのみ、認識の実行が高速化されます。0 * x => C0など。