1

C でプログラムを書いて、除算の繰り返しに関する浮動小数点誤差の大きさを感じてみてください。

#include <stdio.h>

int main (int argc, char* argv[]) {
    if (argc < 3) {
        printf("Enter a decimal number as the first positional " 
                "argument\n");
        printf("Enter the maximum number of digits to print as the " 
                "second positional argument\n");
        return 0;
    }   

    long double d;
    sscanf(argv[1], "%Lf", &d);
    int m;
    sscanf(argv[2], "%d", &m);

    int i;
    char format[10];
    for (i = 1; i <= m; ++i) {
        printf("(%d digits)\n", i); 
        sprintf(format, "%%.%dLf\n\n", i); 
        printf(format, d); 
    }   

    long double p = d;
    printf("\n");
    for (i = 1; i <= m; ++i) {
        printf("(%Lf/10e%d with %d digits)\n", d, i, m); 
        p = p/(long double)10.0;
        printf(format, p); 
    }
    return 0;
}

これは、次の引数を指定して実行した場合の出力の 1 行です。

$ fpe 0.1 700
.
.
.
(0.100000/10e180 with 700 digits)
0.0000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000999999999999999999969819570700939858153376
736698732853283605408116087882762948991724868957176649769045358705872354052
261113540314114885779914335315639806061208847920179776799404948795506248532
485303630811119507604985596684233990126219304092175565232198569923253737561
276484626462077772036038845251286782974821021132356946292172207615386395848
331484216638642723800290357587296443408362280895970909637712494349003491485
594533190659822910753768473307578901199121901299804449081420898437500000000
000000000000000000000000000
.
.
.

ここでは、485 桁の浮動小数点ノイズが観察されます。これは gcc 4.4.3 でコンパイルされたもので、80 ビットの拡張精度を使用していると思われます。ただし、10 進数で 485 桁というのは、80 ビットをはるかに超える情報量です。それで、私の質問は、この情報はどこから来ているのですか?

4

2 に答える 2

5

余分な情報は印刷されません。出力される値は、正確に の値ですp

180回の反復後、p+0x1.A8E90F9908E0CA56p-602、つまり 15309010345804195115•2 -665になります。IEEE 754 標準で、浮動小数点数の値を、符号 (+1 または -1) に 2 の累乗 (数値の指数フィールドによって決定される) を乗算し、その仮数の値 (分数部分)。したがって、すべての浮動小数点数には特定の値があります。上記はの値ですp. In decimal, that value is exactly .9999999999999999999698195707009398581533767366987328532836054081160878827629489917248689571766497690453587058723540522611135403141148857799143353156398060612088479201797767994049487955062485324853036308111195076049855966842339901262193040921755652321985699232537375612764846264620777720360388452512867829748210211323569462921722076153863958483314842166386427238002903575872964434083622808959709096377124943490034914855945331906598229107537684733075789011991219012998044490814208984375•10 -181 .

それがあなたのプログラムによって生み出される価値です。したがって、出力フォーマッタは の値を正確に出力しましたp。それは素晴らしい仕事をしました。

実際、浮動小数点はあらゆる点で優れた機能を果たしました。その値は、10 -181に最も近い long double 値です。ロングダブルではこれ以上近づけません。そのため、数百回の算術演算を行った後でも、エラーは増加しませんでした。

ここには新しい情報はありません。の表現にあるビットを知らされた場合p、同じ数百の 10 進数を生成できたはずです。彼らはあなたに何も新しいことを教えてくれません。ただし、それらもゴミではありません。それらは の値によって正確に決定されますp

于 2012-12-08T04:04:07.187 に答える
0

エリックの優れた答えにさらに情報を追加するために、181回目の反復はあなたが行った方法で計算され、たまたま10 ^ -181に最も近い長い倍精度ですが、それはすべてのnに対して機能しません...

たとえば1/10.0/10.0/10.0/10.0 != 1/10000.0、long double で計算された場合。

squeak Smalltalk http://code.google.com/p/arbitrary-precision-float/で独自の float エミュレーション パッケージを使用すると、最初の 300 個の 10^-n のうち、77 が最も近い long double 値である 223 であると言えます。ではありません。

(1 to: 300) count: [:n |
    ((1 to: n) inject: (1 asArbitraryPrecisionFloatNumBits: 64) into: [:p :i | p/10])
    ~= ((10 raisedTo: n negated) asArbitraryPrecisionFloatNumBits: 64)]

そして差のピークは 10^-218 に対して 4 ulp です。

(1 to: 300) detectMax: [:n |
    (((1 to: n) inject: (1 asArbitraryPrecisionFloatNumBits: 64) into: [:p :i | p/10])
    - ((10 raisedTo: n negated) asArbitraryPrecisionFloatNumBits: 64)) abs
    / (2 raisedTo: -63+((10 raisedTo: n negated) floorLog: 2))].

ulp に関するエラーの進化は次のとおりです。

(1 to: 300) collect: [:n |
    ((((1 to: n) inject: (1 asArbitraryPrecisionFloatNumBits: 64) into: [:p :i | p/10])
    - ((10 raisedTo: n negated) asArbitraryPrecisionFloatNumBits: 64))
    / (2 raisedTo: -63+((10 raisedTo: n negated) floorLog: 2))) asInteger].

#(0  0  0 -1 -1 -1 -1 -1 -1 -2 -1 -1 -1 -1 -1 -1 -1 -1 -2 -2
 -1 -1 -1 -1 -1 -1  0 -1  0  0  0  1  0  0  0  0  1  0  0  0
  0  0  0 -1  0  0  0  1  0  0  0  0  0  0  1  1  1  1  0  0
  0  0 -1  0 -1 -1 -1 -1 -2 -1 -1 -2 -2 -2 -3 -2 -2 -3 -2 -2
 -3 -2 -2 -2 -2 -1 -2 -1 -1 -2 -2 -2 -1 -2 -2 -1 -2 -2 -1 -2
 -2 -2 -3 -2 -1 -2 -2 -1 -2 -2 -1 -2 -2 -2 -3 -2 -2 -1 -1 -1
 -1 -1 -1 -1 -2 -2 -1 -3 -2 -2 -3 -2 -2 -3 -3 -2 -2 -2 -2 -3
 -2 -2 -3 -3 -2 -3 -2 -2 -2 -3 -2 -2 -3 -2 -1 -2 -2 -1 -2 -1
 -1 -2 -1 -1 -1  0  0  0  0  0  1  1  0  0  0  0  0  1  0  0
  0  0  0  0 -1 -1 -1 -1  0  0  0  0 -1 -1 -1 -2 -1  0 -1 -1
 -1 -1 -1 -2 -1 -1 -1 -1 -2 -2 -2 -2 -2 -2 -3 -3 -2 -4 -3 -2
 -3 -2 -2 -3 -2 -2 -2 -2 -1 -3 -2 -2 -3 -3 -2 -1 -2 -2 -1 -2
 -2 -1 -3 -2 -2 -3 -3 -2 -3 -2 -1 -1 -1  0  0  0  0  0 -1  0
  0 -1  0  0 -1  0  0  0  0  0 -1 -1  0  0 -1 -1 -1 -1  0  0
  0  1  1  1  0  1  1  1  1  0  1  1  1  1  1  1  0  0  0  0)
于 2012-12-11T18:39:36.830 に答える