2

C++ で 3 つの確率の単純なセットを表現しようとしています。例えば:

a = 0.1  
b = 0.2  
c = 0.7

(私が知る限り、確率は合計して 1 にならなければなりません)

私の問題は、C++ で 0.7 を float として表現しようとすると、最終的に 0.69999999 になってしまうことです。これは、後で計算を行うときに役に立ちません。0.8、0.80000001 も同様です。

C++ で 0.0 と 1.0 の間の数値を表すより良い方法はありますか?

これは、数値がメモリに格納される方法に関連しているため、値が正しいかどうかをテストする場合、それらがどのように表示/印刷されるかは気にしません。

4

11 に答える 11

22

これはC++とは関係がなく、浮動小数点数がメモリ内でどのように表されるかと関係があります。浮動小数点値を比較するために等式演算子を使用しないでください。より良い方法については、ここを参照してください:http ://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

于 2009-11-20T19:04:56.047 に答える
14

私の問題は、C ++で0.7をfloatとして表現しようとすると、0.69999999になってしまうことです。これは、後で計算を行うときに役立ちません。0.8、0.80000001についても同じです。

それは本当に問題ですか?より高い精度が必要な場合は、floatの代わりにdoubleを使用してください。これにより、ほとんどの作業に十分な約15桁の精度が得られるはずです。

ソースデータを検討してください。0.7は0.69999999よりも本当に大幅に正しいですか?

その場合、次のような有理数ライブラリを使用できます。

http://www.boost.org/doc/libs/1_40_0/libs/rational/index.html

問題が確率の合計が定義上1になることである場合は、最後の1つを省略して、それらを数値のコレクションとして格納します。1から他の合計を引くことによって最後の値を推測します。

于 2009-11-20T19:06:25.163 に答える
8

どのくらいの精度が必要ですか?値をスケーリングし、固定小数点表現で量子化することを検討してください。

于 2009-11-20T19:03:02.567 に答える
2

本当に精度が必要で、有理数に固執しているのであれば、固定小数点の不動点を使用できると思います。私はこれまでこれを行ったことがないので、どのライブラリもお勧めできません。

または、fp番号を比較するときにしきい値を設定することもできますが、どちらか一方の側でエラーを発生させる必要があります。

bool fp_cmp(float a, float b) {
    return (a < b + epsilon);
}

過剰な精度は計算ごとに自動的に切り捨てられるため、アルゴリズムでさまざまな桁数で操作する場合は注意が必要です。説明するための考案された例:

a = 15434355e10 + 22543634e10
b = a / 1e20 + 1.1534634
c = b * 1e20

c = b + 1.1534634e20

2つの結果は大きく異なります。最初の方法を使用すると、最初の2つの数値の精度の多くが1e20による除算で失われます。必要な最終値が1e20のオーダーであると仮定すると、2番目の方法でより正確になります。

于 2009-11-20T19:11:54.793 に答える
2

あなたがあなたの番号でやりたいテストは正しくありません。

0.1のような数の基数2の記数法には、無限の周期的な数であるため、正確な浮動小数点表現はありません。3分の1を考えてみましょう。これは、基数3のシステムでは0.1として正確に表現できますが、基数10のシステムでは0.333...です。

したがって、浮動小数点数が0.1の場合、テストに欠陥が生じる可能性があります。

解決策は、有理数(ブーストには有理数があります)を使用することです。これは、ermm、有理数に対して常に正確であるか、数値に10の累乗を掛けて自作の10進数システムを使用します。

于 2009-11-20T19:13:32.763 に答える
1

バイナリマシンは、常に小数部(.0および.5、.25、.75などを除く)を浮動小数点で正確に表現されていない値に丸めます。これはC++言語とは何の関係もありません。コード内で数値の観点から対処する以外に、実際の方法はありません。

あなたが求める確率を実際に生み出すことに関して:

float pr[3] = {0.1, 0.2, 0.7};
float accPr[3];
float prev = 0.0;
int i = 0;

for (i = 0; i < 3; i++) {
    accPr[i] = prev + pr[i];
    prev = accPr[i];
}

float frand = rand() / (1 + RAND_MAX);
for (i = 0; i < 2; i++) {
    if (frand < accPr[i]) break;
}
return i;
于 2009-11-20T19:11:16.700 に答える
1

数桁の精度のみが必要な場合は、整数を使用してください。より高い精度が必要な場合は、精度を保証する別のライブラリを探す必要があります。

于 2009-11-20T19:08:57.863 に答える
1

ここでの問題は、浮動小数点数が基数 2 で格納されることです。基数 10 の 10 進数を基数 2 の浮動小数点数で正確に表すことはできません。

少し戻りましょう。.1 とはどういう意味ですか? それとも.7?1x10 -1と 7x10 -1を意味します。通常の 10 進数ではなく2 進数を使用している場合、.1 は 1x2 -1または 1/2 を意味します。.11 は、1x2 -1 + 1x2 -2、または 1/2+1/4、または 3/4 を意味します。

このシステムでは、分母が常に 2 のべき乗であることに注意してください。2 のべき乗である分母がなければ、有限の桁数で数値を表すことはできません。たとえば、.1 (10 進数) は 1/10 を意味しますが、無限に繰り返される分数である 2 進数では、0.000110011... (0011 パターンが永遠に繰り返されます)。これは、基数 10 で 1/3 が無限分数 (0.3333....) であるのと似ています。基数 10 は、分母が 2 と 5 の累乗の倍数である数値のみを正確に表すことができます。 60 は 2、3、4、および 5 で割り切れますが、何らかの理由で 10 進数を使用し、コンピューターでは 2 進数を使用します)。

浮動小数点数 (または固定小数点数) の桁数は常に有限であるため、これらの無限に繰り返される小数を正確に表現することはできません。そのため、実際の値にできるだけ近づけるために値を切り捨てるか丸めますが、実際の値と正確には等しくありません。これらの丸められた値を合計し始めると、より多くのエラーが発生し始めます。10 進数では、1/3 の表現が .333 の場合、その 3 つのコピーを合計すると、1 ではなく .999 になります。

考えられる解決策は 4 つあります。.1 や .7 のような小数を正確に表すことだけが重要な場合 (たとえば、1/3 に言及したのと同じ問題が発生することは気にしません)、数値を小数として表すことができます。バイナリコード化された 10 進数、およびそれらを操作します。これは、多くの演算が 10 進数で定義される金融では一般的なソリューションです。これには、コンピュータの FPU の利点を利用せずに独自の算術演算をすべて自分で実装するか、10 進算術ライブラリを見つける必要があるという欠点があります。これも、前述のように、10 進数で正確に表すことができない分数には役立ちません。

別の解決策は、分数を使用して数値を表すことです。分数を使用し、分子と分母に bignum (任意に大きな数) を使用すると、コンピューターのメモリに収まる任意の有理数を表すことができます。繰り返しになりますが、マイナス面は算術が遅くなることです。自分で算術を実装するか、既存のライブラリを使用する必要があります。これはすべての有理数の問題を解決しますが、π または √2 に基づいて計算された確率で終わった場合でも、それらを正確に表すことができないという同じ問題があり、1 つも使用する必要があります。後のソリューションの。

3番目の解決策は、数値を正確に1に加算することだけが重要な場合、n個の可能性があるイベントに対して、それらの確率のn -1の値のみを保存し、最後の確率を次のように計算することです。 1 から残りの確率の合計を引いたもの。

そして 4 番目の解決策は、浮動小数点数 (または無理数を表すために使用される分数などの不正確な数) を扱うときに常に覚えておく必要があることを行い、2 つの数値が等しいかどうかを決して比較しないことです。再び基数 10 で、1/3 の 3 つのコピーを合計すると、.999 になります。その数を 1 と比較したい場合は、代わりに比較して 1 に十分近いかどうかを確認する必要があります。差の絶対値 1 ~ .999 が .01 などのしきい値未満であることを確認します。

于 2009-11-20T19:52:52.237 に答える
0

アプリケーションの要件に応じて、いくつかのソリューションのいずれかが最適になる可能性があります。

  1. あなたは本質的に精度の欠如を抱えて生きており、フロートまたはダブルを使用しています。どちらも同等かどうかをテストすることはできません。これは、確率の合計が1.0と等しいかどうかをテストできないことを意味します。

  2. 前に提案したように、固定の精度が必要な場合は整数を使用できます。0.7を7、0.1を1、0.2を2と表すと、合計で10、つまり1.0になります。確率で計算する必要がある場合、特に除算と乗算を行う場合は、結果を正しく丸める必要があります。これにより、再び不正確さが生じます。

  3. 整数のペア(1,2)= 1/2=0.5の分数として数値を表します。正確で、2)よりも柔軟性がありますが、それらを使用して計算する必要はありません。

  4. ずっと進んで、有理数を実装するライブラリ(gmpなど)を使用できます。正確、任意精度で、それを使用して計算できますが、低速です。

于 2009-11-20T21:01:17.060 に答える
0

申し訳ありませんが、あなたの問題に対する簡単な答えはありません。

これは、これらのタイプの問題を扱う「数値解析」と呼ばれる研究分野に分類されます (これは、2 つの浮動小数点値が等しいかどうかを確認するだけではありません)。そして、研究分野ごとに、それを扱った書籍、雑誌記事、コースなどがたくさんあることを意味します。博士論文を書いている人もいます。

私が言えることは、問題と解決策はしばしば非常に直感的ではないため、これらの問題にあまり対処する必要がないことに感謝しているということです.

作業中の数値と計算を表現するために必要なことは、実行している操作、それらの操作の順序、およびそれらの操作で処理することが期待される値の範囲に大きく依存します。 .

于 2009-11-20T19:21:14.760 に答える
-5

ええ、私はあなたがそのようなことを心配しているなら、あなたが必要とする数(0-100)(0-1000)またはあなたが必要とするどんな固定サイズでもスケーリングするでしょう。また、ほとんどの場合、数学の計算が高速になります。古き良き時代には、cos / sineテーブル全体とその他のそのようなblehを整数形式で定義して、フローティングファズを減らし、計算速度を上げていました。

「0.7」がストレージ上でそのようにファジーするのは少し面白いと思います。

于 2009-11-20T19:15:12.843 に答える