2

さあ、

float dt;

dt私はテキストファイルから次のように読みました

inputFile >> dt;

次に、次のようなforループがあります。

for (float time=dt; time<=maxTime; time+=dt)
{
    // some stuff
}

私が得たdt=0.05出力をすると、std::cout << time << std::endl;

0.05
0.10
...
7.00001
7.05001
...

では、なぜしばらくして桁数が増えているのでしょうか。

4

3 に答える 3

6

すべての数値をIEEE754浮動小数点値で表すことができるわけではないためです。ある時点で、あなたは完全に表現できない数を得るでしょう、そしてコンピュータは最も近いものを選ばなければならないでしょう。

IEEE754-1985のWikipediaエントリに0.05を入力しHarald Schmidt's excellent online converterて参照すると、次のビットになります(私の説明は次のとおりです)。

   s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm
   0 01111010 10011001100110011001101
     |||||||| |||||||||||||||||||||||
128 -+||||||| ||||||||||||||||||||||+- 1 / 8388608
 64 --+|||||| |||||||||||||||||||||+-- 1 / 4194304
 32 ---+||||| ||||||||||||||||||||+--- 1 / 2097152
 16 ----+|||| |||||||||||||||||||+---- 1 / 1048576
  8 -----+||| ||||||||||||||||||+----- 1 /  524288
  4 ------+|| |||||||||||||||||+------ 1 /  262144
  2 -------+| ||||||||||||||||+------- 1 /  131072
  1 --------+ |||||||||||||||+-------- 1 /   65536
              ||||||||||||||+--------- 1 /   32768
              |||||||||||||+---------- 1 /   16384
              ||||||||||||+----------- 1 /    8192
              |||||||||||+------------ 1 /    4096
              ||||||||||+------------- 1 /    2048
              |||||||||+-------------- 1 /    1024
              ||||||||+--------------- 1 /     512
              |||||||+---------------- 1 /     256
              ||||||+----------------- 1 /     128
              |||||+------------------ 1 /      64
              ||||+------------------- 1 /      32
              |||+-------------------- 1 /      16
              ||+--------------------- 1 /       8
              |+---------------------- 1 /       4
              +----------------------- 1 /       2

符号は0であり、正です。指数は、左側の数値への1ビットマッピングで示されます。64+32+16+8+2 = 122 - 127 bias = -5したがって、乗数は2-5または1/32です。バイアスは、非常に小さい数の127表現を可能にすることです(大きな大きさの負の数ではなく、ゼロに近い場合など)。

仮数はもう少し複雑です。1ビットごとに、右側に数値を累積します(暗黙的に追加した後1)。したがって、数をの合計として計算できます{1, 1/2, 1/16, 1/32, 1/256, 1/512, 1/4096, 1/8192, 1/65536, 1/131072, 1/1048576, 1/2097152, 1/8388608}

これらすべてを合計すると、が得られます1.60000002384185791015625

これに乗数(指数ビットから以前に計算された)を掛ける、が得られるので、それがすでに正確に表されていないことがわかります。仮数のこのビットパターンは実際にはと同じですが、指数が-5ではなく-4であるため、が等しくなることはめったにありません(これはお気に入りのインタビューの質問のようです)。1/320.05000000010.050.10.1 + 0.1 + 0.10.3

それらを合計し始めると、その小さなエラーが累積されます。これは、0.05それ自体にエラーが表示されるだけでなく、累積の各段階でエラーが発生する可能性があるためです。すべての数値、、0.1など0.150.2表すことができるわけではありません。まさにどちらか。

最終的に、デフォルトの精度を使用すると、エラーが十分に大きくなり、数値に表示されるようになります。次のような独自の精度を選択することで、これを少し延期することができます。

#include <iostream>
#include <iomanip>
:
std::cout << std::setprecison (2) << time << '\n';

変数値は修正されませんが、エラーが表示される前に、より多くの呼吸スペースが与えられます。

余談std::endlですが、バッファのフラッシュを強制するため、避けることをお勧めする人もいます。実装がそれ自体で動作している場合、とにかく改行を送信すると、端末デバイスでこれが発生します。また、標準出力を非終端記号にリダイレクトした場合は、おそらくすべての行でフラッシュする必要はありません。あなたの質問とはあまり関係がなく、おそらくほとんどの場合、実際の違いはないでしょう。私が提起したいと思った点です。

于 2012-02-25T11:35:14.110 に答える
3

IEEEフロートは2進数システムを使用するため、10進数を正確に格納することはできません。それらのいくつかを一緒に追加すると(場合によっては2つで十分です)、表現エラーが蓄積されて表示される可能性があります。

于 2012-02-25T11:33:58.163 に答える
0

一部の数値は、浮動小数点または2進数を使用して正確に表すことができません。私が正しく覚えているなら、そのような数の1つは10進数の0.05です(基数2では、小数が無限に繰り返されます)。もう1つの問題は、浮動小数点をファイルに(基数10の数値として)出力してから読み戻すと、異なる数値が得られる可能性があることです。これは、基数が異なり、小数のbase2を小数のbase10の数値に変換するときに問題が発生する可能性があるためです。

より高い精度が必要な場合は、bignumライブラリを検索してみてください。ただし、これはフローティングポイントよりもはるかに遅く動作します。精度の問題に対処する別の方法は、数値を「共通の分数」として数値化子/分母(つまり、0.1の代わりに1/10、0.333の代わりに1/3など)として格納することです。おそらくそれでもライブラリがありますが、聞いたことがありません)が、pieのような無理数では機能しません。

于 2012-02-25T11:59:45.960 に答える