2012-09-12 7 views
10

小さな精度の浮動小数点で算術演算を実行すると、異常な計算時間が検出されました。単純なコード展示以下この動作:Yで算術演算に通常よりも時間がかかる理由を教えてください。

  • = 0.5

    X = 0.000000
    時間:1.32000segsここ

    #include <time.h> 
    #include <stdlib.h> 
    #include <stdio.h> 
    
    const int MAX_ITER = 100000000; 
    
    int main(int argc, char *argv[]){ 
        double x = 1.0, y; 
        int i; 
        clock_t t1, t2; 
        scanf("%lf", &y); 
        t1 = clock(); 
        for (i = 0; i < MAX_ITER; i++) 
         x *= y; 
        t2 = clock(); 
        printf("x = %lf\n", x); 
        printf("Time: %.5lfsegs\n", ((double) (t2 - t1))/CLOCKS_PER_SEC); 
        return 0; 
    } 
    

    は、プログラムの2回の異なる実行であります

  • Y = 0.9

    X = 0.000000
    時間と共に:19.99000segs

Iコードをテストするために、以下の仕様を有するラップトップを使用しています:

  • CPU:インテル®Core™2 DuoプロセッサCPUのT5800する@ 2.00GHz×2
  • RAM:4ギガバイト
  • OS:Ubuntuの12.04(64ビット)
  • モデル:Dellのスタジオ1535

この現象が発生した理由を誰かが詳細に説明してもらえますか?私は、y = 0.9の場合、xの値がy = 0.5の場合よりもゆっくりと0になることを認識しているので、この問題は直接この問題に関係していると考えられます。

+0

0になる前に、2番目のケースで不規則なものが増えている可能性があります。 –

+1

denormalsのパフォーマンスへの影響についての[この回答](http://stackoverflow.com/a/9314926/1011995)も参照してください。 –

答えて

10

非正規化(または、むしろ非正規化)数値は、多くの場合、パフォーマンスが低下します。 2番目の例でゆっくりと0に収束すると、より多くの準異常が生成されます。詳細はherehereです。より深刻な読書のためには、引用符付きの(そして非常に密集した)What Every Computer Scientist Should Know About Floating-Point Arithmeticをチェックしてください。以下のように、浮動小数点数は、バイナリで表現されているIEEE-754の下

:第2供給源由来

同じ番号を表す複数の方法が潜在的にあり

Number = signbit \* mantissa \* 2exponent

、 例として小数点を使用すると、数字は 1 * 10-1または0.1 * 100または0.01 * 10と表すことができます。標準では、 の数値は常にf最初のビットを1にします。小数では、 は1 * 10-1の例に対応します。

ここで、表現できる最も低い指数が-100であるとします。 したがって、通常の形式で表現できる最小の数字は 1 * 10-100です。しかし、先導ビットが であるという制約を緩和すると、同じ 空間内のより小さい数を実際に表すことができます。小数点の例をとると、0.1 * 10-100を表すことができます。この は非正規数と呼ばれます。異常な数値を持つ目的は、最小の正規数とゼロとの間のギャップを滑らかにすることです。

正常な数よりも精度が劣る であることを理解することは非常に重要です。実際には、彼らはより小さいサイズのために の精度を減らして取引しています。したがって、 の非正常な数値を使用する計算は、通常の数値に対する の計算と同じ精度を持つことはありません。したがって、正規化されていない数値の計算が であるアプリケーションは、おそらく の価値があり、再スケーリング(つまり、数値に の倍率で乗算する)がサブノーマルを少なくし、より正確に の結果を出すかどうかを調べる価値があります。

私はそれを自分自身で説明しようと考えていましたが、上記の説明は非常によく書かれ、簡潔です。

+1

これはまさに私が探していた答えです、ありがとう! – jace

3

0.9^nはもっとゆっくり数学的に0.5^nよりも0に収束するが、理由はIEEE-754浮動小数点の実装では、それがすべてで0に収束しないので、あなたは、測定可能な差異がない得ます。

最小の正doubleにIEEE-754の表現はy = 0.5と、ループ53の非正規数に遭遇するように最小の正の通常は、2 -1021であり、2 -1074です。最小正の正規化が達成されると、次の積は2 -1075になりますが、ラウンドタイとラストビットスルーのデフォルトの丸めモードは0に丸められます(IEEE-754 表現浮動小数点数とデフォルトのラウンドタイ・トゥ・ラスト・ビット・ゼロの丸めモードは、標準が完全に実装されていなくても、標準のコンシューマ・ハードウェア上では非常に普及しています)。それ以来、あなたには倍数0*yがあります。通常の浮動小数点乗算(それはyが異常な数であっても速いです)。 0.5 < y < 1

あなたは(正)非正規範囲の下限に到達したら、xの値にx*yラウンドの結果は、再び(y = 0.9ために、反復の固定点は、5 * 2 です - 1074)。それは数千回の反復(0.9^7 < 0.5)の後で達成されるので、基本的には、非正規数にループ全体の非ゼロ数を掛けています。多くのプロセッサでは、このような乗算は直接処理することができず、マイクロコードで処理する必要があります。

スピードがIEEE-754セマンティクスよりも重要である場合(またはそれらが他の理由で望ましくない場合)、多くのコンパイラはその動作を無効にし、ハードウェアがサポートする場合には異常な数値を0にフラッシュするオプションを提供します。明示的に私のgccのマニュアルページでオプションを見つけることができませんでしたが、-ffast-mathはここでトリックを行いました。

+0

正確な計算で問題を分析し、最適化するコンパイラオプションを提供することが可能であることを証明するための名誉です。なぜ、0.5 jace

+1

'x'が最小の正の正規方程式で、' y> 0.5'の場合、 'x * y'の正確な値は' x/2'よりも大きいので、それは同値ではなく、 「x」と「0」との間の「x」である。 'y 'が0.5よりも十分に大きい場合、それは' x'が最小の準正常な 's'に達する前に起こります。 'y = 0.75'の場合、あなたは'(2 * s)* y = 1.5 * s'を数学的に持つので、 's'と' 2 * s'のちょうど中間にあります。 'y * 0.9'と' x = 5 * s'の場合、 '4.5 * s'を得ますが、最も近い' double'は0.9よりわずかに大きく、だからそれは切り上げる。 –

+0

ありがとう、私はこの低レベルの詳細については知らなかった。 – jace

関連する問題