10

精度の低い浮動小数点数で算術演算を実行すると、異常な計算時間が検出されました。次の単純なコードは、この動作を示します。

#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.5の場合

    x = 0.000000
    時間:1.32000セグメント

  • y=0.9の場合

    x = 0.000000
    時間:19.99000セグメント

コードをテストするために、次の仕様のラップトップを使用しています。

  • CPU:Intel®Core™2Duo CPU T5800@2.00GHz×2
  • RAM:4 GB
  • OS:Ubuntu 12.04(64ビット)
  • モデル:Dell Studio 1535

誰かがこの動作が発生する理由を詳細に説明できますか?y = 0.9の場合、x値はy = 0.5の場合よりもゆっくりと0になることを知っているので、問題はこれに直接関係していると思われます。

4

2 に答える 2

10

非正規化数(または非正規化数)は、多くの場合、パフォーマンスに影響を与えます。2番目の例のように、ゆっくりと収束すると0、より多くの非正規化数が生成されます。詳細はこちらこちらをご覧ください。より真剣に読むには、すべてのコンピューター科学者が浮動小数点演算について知っておくべき、よく引用される(そして非常に密度の高い)ことを確認してください。

2番目のソースから:

IEEE-754では、浮動小数点数は2進数で次のように表されます。

Number = signbit \* mantissa \* 2exponent

同じ数値を表す方法は複数ある可能性があります。例として10進数を使用すると、数値0.1は1*10-1または0.1*100、さらには0.01 * 10として表すことができます。標準では、数値は常に1として最初のビット。1*10-1の例に対応する10進数。

ここで、表現できる最小の指数が-100であると仮定します。したがって、正規形で表すことができる最小の数は1*10-100です。ただし、先頭ビットが1であるという制約を緩和すると、実際には同じスペースで小さい数値を表すことができます。10進数の例をとると、0.1*10-100を表すことができます。これは非正規化数と呼ばれます。非正規化数を持つ目的は、最小の正規数とゼロの間のギャップを滑らかにすることです。

非正規化数は通常の数値よりも精度が低いことを理解することが非常に重要です。実際、彼らはより小さなサイズのために精度を下げて取引しています。したがって、非正規化数を使用する計算は、正規数の計算と同じ精度にはなりません。したがって、非正規化数で重要な計算を行うアプリケーションは、再スケーリング(つまり、数値に何らかのスケーリング係数を掛ける)によって非正規化数が少なくなり、より正確な結果が得られるかどうかを調べる価値があります。

自分で説明しようかと思っていたのですが、上記の説明は非常によく書かれていて簡潔です。

于 2012-09-12T17:44:57.543 に答える
3

0.9^n数学よりもゆっくりと0に収束する0.5^nためではなく、IEEE-754浮動小数点実装では0に収束しないため、測定可能な違いが得られます。

doubleIEEE-754表現の最小の正は2-1074であり、最小の正の法線は2 -1021であるため、を使用するとy = 0.5、ループは53の非正規化数に遭遇します。最小の正の非正規化数に達すると、次の積は2 -1075になりますが、0に丸められる最後のビットゼロへの丸めのデフォルトの丸めモードが原因です(浮動小数点数のIEEE-754表現)また、デフォルトの丸めから最後のビットゼロへの丸めモードは、標準が完全に実装されていない場合でも、標準のコンシューマーハードウェアでほぼ遍在します。)それ以降0*y、通常の浮動小数点乗算である乗算があります。 (それyは非正規化数であっても速いです)。

を使用0.5 < y < 1すると、(正の)非正規範囲の下限に達すると、結果は再びx*yの値に丸められます(の場合、反復の固定小数点は5 * 2 -1074です)。これは、数千回の反復()の後に到達するため、基本的に、ループ全体で非正規化数にゼロ以外の数を掛けることになります。多くのプロセッサでは、このような乗算を直接処理することはできず、マイクロコードで処理する必要がありますが、これは非常に低速です。xy = 0.90.9^7 < 0.5

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

于 2012-09-12T19:31:35.830 に答える