6

問題の説明

私の流体シミュレーション中、物理的な時間は次のように進行してい0, 0.001, 0.002, ..., 4.598, 4.599, 4.6, 4.601, 4.602, ...ます。ここで、この時系列からtime =を選択0.1, 0.2, ..., 4.5, 4.6, ...して、さらに分析を行います。fractpartそこで、ヒットがゼロかどうかを判断するために次のコードを作成しました。

しかし、私は非常に驚いて、次の2つの除算方法で2つの異なる結果が得られていることに気付きました。どうすればよいですか?

double param, fractpart, intpart;
double org = 4.6;
double ddd = 0.1;

// This is the correct one I need. I got intpart=46 and fractpart=0
// param = org*(1/ddd);

// This is not what I want. I got intpart=45 and fractpart=1
param = org/ddd;

fractpart = modf(param , &intpart);
Info<< "\n\nfractpart\t=\t"
    << fractpart
    << "\nAnd intpart\t=\t"
    << intpart
    << endl;

なぜそれがこのように起こるのですか?
そして、皆さんが私を少し容認するなら、私は大声で叫ぶことができます:「C ++委員会はこれについて何かをすることができますか?これは混乱しているからです。」:)

カットオフエラーの影響を回避するために正しい余りを取得するための最良の方法は何ですか?fmodはより良い解決策ですか?ありがとう

の答えに応答する

デビッドシュワルツ

double aTmp = 1;
double bTmp = 2;
double cTmp = 3;
double AAA = bTmp/cTmp;
double BBB = bTmp*(aTmp/cTmp);
Info<< "\n2/3\t=\t"
    << AAA
    << "\n2*(1/3)\t=\t"
    << BBB
    << endl;

そして、私は両方を手に入れました、

2/3     =       0.666667
2*(1/3) =       0.666667
4

4 に答える 4

7

浮動小数点値はすべての可能な数値を正確に表すことはできないため、数値は概算されています。これにより、計算で使用した場合に異なる結果が得られます。

浮動小数点数を比較する必要がある場合は、等しいかどうかをテストするのではなく、常に小さなイプシロン値を使用する必要があります。あなたの場合、私は最も近い整数に丸め(切り捨てではなく)、元の値からそれを引き、結果のabs()をイプシロンと比較します。

質問が、なぜ合計が異なるのかという場合、簡単な答えは、それらが異なる合計であるということです。より長い説明のために、ここに関係する数の実際の表現があります:

             org:  4.5999999999999996 = 0x12666666666666 * 2^-50
             ddd: 0.10000000000000001 = 0x1999999999999a * 2^-56
           1/ddd:                  10 = 0x14000000000000 * 2^-49
   org * (1/ddd):                  46 = 0x17000000000000 * 2^-47
       org / ddd:  45.999999999999993 = 0x16ffffffffffff * 2^-47

どちらの入力値もdoubleで正確に表されておらず、それぞれが最も近い値に切り上げまたは切り捨てられていることがわかります。orgシーケンスの次のビットが0dddになるため、切り捨てられました。そのシーケンスの次のビットが1になるため、切り上げられました。

このため、数学演算が実行されると、演算と元の数値の丸め方法に応じて、丸めがキャンセルまたは累積される可能性があります。

この場合、1/0.1はたまたま正確に10に丸められます。

10を掛けるorgと、たまたま切り上げられます。

除算orgdddたまたま切り捨てられます(「発生する」と言いますが、切り捨てられた数値を切り上げられた数値で除算しているため、結果が少なくなるのは当然です)。

入力が異なれば、丸めも異なります。

これはほんのわずかなエラーであり、小さなイプシロンでも簡単に無視できます。

于 2012-12-17T17:00:18.753 に答える
1

私があなたの質問を正しく理解しているなら、それはこれです:なぜ、限られた精度の算術でX/Y、同じではないのX * (1/Y)ですか?

そして、その理由は単純です。たとえば、6桁の小数精度を使用することを検討してください。これはdouble実際に行われていることではありませんが、概念はまったく同じです。

小数点以下6桁で、1/3.333333です。しかし2/3です.666667。それで:

2 / 3 = .666667  

2 * (1/3) = 2 * .333333 = .6666666  

これは、固定精度の数学の性質です。この動作を許容できない場合は、精度が制限された型を使用しないでください。

于 2012-12-17T17:08:19.430 に答える
0

4.6正確に表現することはできません:http: //www.binaryconvert.com/result_double.html?decimal = 052046054

整数部分と小数部分を分離する前に、丸めを使用してください。

アップデート

rationalBoostライブラリのクラスを使用することをお勧めします: http ://www.boost.org/doc/libs/1_52_0/libs/rational/rational.html

あなたの仕事について

必要なものを見つけるdoubleには、たとえば、精度を考慮に入れて、4.6それに「近さ」を計算します。

double time;

...

double epsilon = 0.001;

if( abs(time-4.6) <= epsilon ) {
    // found!
}
于 2012-12-17T17:14:43.177 に答える
0

何を達成したいのかよくわかりませんが、値を取得してから1/1000の範囲で調整を行いたい場合は、float / doubleの代わりに整数を使用してみませんか?

1000である除数があり、除数を掛ける必要がある値を反復処理します。

だからあなたは次のようなものを手に入れるでしょう

double org = ... // comes from somewhere
int divisor = 1000;
int referenceValue = org * div;
for (size_t step = referenceValue - 10; step < referenceValue + 10; ++step) {
   // use   (double) step / divisor to feed to your algorithm
}
于 2012-12-17T17:26:31.503 に答える