0

重複の可能性:
Objective-C のフロートに関する問題

基本的な問題のように聞こえるかもしれませんが、私はそれを理解するために 2 日間努力しています。解決策を探しましたが、見つかった唯一の説明は、float が 4 バイトのメモリしか使用しないということです。(それは役に立ちます...)次のループがあります:

double x = 0.0;
        for(int i = 0; i< 100; i++)
        {
            x = x + 0.01;
            NSLog(@"%f",x);
        }

そしてそれは印刷します:

0.010000
0.020000
0.030000
.
.
1.000000

しかし、double を float に変更すると:

0.010000
0.020000
0.030000
.
.
0.820000
0.830000
0.839999
0.849999
.
.
0.999999

ご覧のとおり、コンピューターは 0.840000 を float として計算できません --- 問題は、0.0 から 1.0 までの float 値を取ることができる UIProgressView を使用しているため、float を使用する必要があることです。

double を使用できない場合、どうすればよいですか? ありがとう。

4

2 に答える 2

3

x = (i + 1) * 0.01;の代わりに使用しx = x + 0.01;ます。floatを正確に表すことができないため、まだ不正確です0.84が、少なくともエラーは蓄積されません。

于 2013-01-30T16:03:01.013 に答える
3

次のコードを書いたらどうなるでしょうか。

int x = 0;
for(int i = 0; i< 100; i++)
{
    x = x + 3.5;
    NSLog(@"%d",x);
}

3.5印刷されなく7.0ても驚かないでしょう10.5か? 「int は不正確」だと思いますか? もちろん違います。

あなたの例ではまったく同じことが起こっています。3.5整数として表現できないのと同じよう0.01に、double として表現できません。実際に得られる値は次のとおりです。

0.01000000000000000020816681711721685132943093776702880859375

ここで、 double への丸めから最初の表現誤差が蓄積されるだけでなく、すべての中間合計が表現可能ではないため、丸め誤差も発生します。この 2 番目のエラーの原因は、この例では発生しません。計算される実際の中間合計は次のとおりです。0.1int

0.01000000000000000020816681711721685132943093776702880859375
0.0200000000000000004163336342344337026588618755340576171875
0.0299999999999999988897769753748434595763683319091796875
0.040000000000000000832667268468867405317723751068115234375
0.05000000000000000277555756156289135105907917022705078125
0.060000000000000004718447854656915296800434589385986328125
...
0.990000000000000657252030578092671930789947509765625
1.0000000000000006661338147750939242541790008544921875

書式指定子を使用してこれらを小数点以下 6 桁%fに丸めると、丸めの 3 番目のソースが得られますが、エラーはすべて十分に小さいため、「期待する」結果が出力されます。

floatの代わりにdouble;を使用するとどうなるか見てみましょう。C 算術オペランド プロモーション規則により、加算はすべて で実行されますdoubleが、結果はfloat各加算後に に丸められます。さらに丸めが行われます。中間値のシーケンスは次のとおりです。

0.00999999977648258209228515625
0.0199999995529651641845703125
0.02999999932944774627685546875
0.039999999105930328369140625
0.0500000007450580596923828125
0.060000002384185791015625
...
0.809999525547027587890625
0.819999516010284423828125
0.829999506473541259765625

この時点まで、エラーは十分に小さいため、10 進数 6 桁に丸めた場合でも、すべてのエラーが「期待される」値を生成します。ただし、次に計算される値は

0.839999496936798095703125

これは、小数点以下 6 桁に丸める正確な中間ケースよりも小さいためです。

0.8399995

切り捨てられ、出力される数値は次のとおりです。

0.8399999

さて、あなたはそれについて何ができますか?6 桁の 10 進数で印刷するとエラーが最終的に十分に大きくなる理由は、順次追加するたびにエラーを累積しているためです。この累積を避けることができれば、誤差は小さいままでこの問題は発生しません。この累積エラーを回避する簡単な方法がいくつかあります。おそらく最も簡単なのは次のとおりです。

for (int i=0; i<100; i++) {
    float x = (i+1)/100.f;
    NSLog(@"%d",x);
}

i + 1との両方100.fが で正確に表されているため、これは機能しfloatます。したがって、除算で発生する丸めは 1 回だけであるため、float結果は可能な限り目的の数値に近くなります。これ以上近づく方法はありません。

于 2013-01-30T16:26:04.680 に答える