2

ユーザーが入力した金額 (米国の通貨) を構成するために使用される米国のコインの最小額をユーザーに返すプログラムを作成しようとしています。

私の問題: プログラムが 0.1 に達したとき、プログラムは 10 セント硬貨を差し引くのではなく、1 セント硬貨と 5 セントを差し引きます。これは、1.85 より大きい数値でのみ発生します。1.85 未満の場合、1 セント硬貨が正常に減算されます。

これが私のコードです:

 while (Money >= 0.25){
   Money = Money - 0.25;
   Coins = Coins + 1;
   printf ("Current money: %f \n", Money);
 }
while (Money >= 0.1) { 
  Money = Money - 0.1;
  Coins = Coins + 1;
  printf ("Current money: %f \n", Money);
}
while (Money >= 0.05) {
 Money = Money - 0.05;
 Coins = Coins + 1;
 printf ("Current money: %f \n", Money);
}
while (Money >= 0.01) {
 Money = Money - 0.01;
 Coins = Coins + 1;
 printf ("Current money: %f \n", Money);
}

数値 2.1 を使用した場合の出力は次のとおりです。

2.1
Current money: 1.850000 
Current money: 1.600000 
Current money: 1.350000 
Current money: 1.100000 
Current money: 0.850000 
Current money: 0.600000 
Current money: 0.350000 
Current money: 0.100000 
Current money: 0.050000 
Current money: 0.040000 
Current money: 0.030000 
Current money: 0.020000 
Current money: 0.010000 
Used 13 

そして、これは数値1.85を使用したときの私の出力です:

1.85
Current money: 1.600000 
Current money: 1.350000 
Current money: 1.100000 
Current money: 0.850000 
Current money: 0.600000 
Current money: 0.350000 
Current money: 0.100000 
Current money: 0.000000 
 Used 8 

なぜこうなった?ダイムが 1.85 より大きい数で使用されないのはなぜですか?

4

4 に答える 4

4

0.1 は必ずしも 0.10000000000000000... を意味するわけではないためです。に表示されている精度の数値のみprintfが表示され%fます。これをステップ実行してデバッガーで値を見ると、おそらく 2.1 ループの最後で、値が 0.099999999 のようになり、0.10 未満であることがわかります。

doubleこれが、通貨に浮動小数点値 ( ) を使用しない理由です。decimal代わりに、バイナリ浮動小数点値に依存しないC# の数値のようなものを使用する必要があります。これはC++ での実装です

あなたの場合、セントの整数を保持するだけで (ドルを得るには 100 で割る必要があることを理解すると)、計算が正確になります。

int money = 281;  // $2.81

while (money >= 25) {    // Quarter
   money -= 25;
   coins++;
   printf("Current money: $%d.%d \n", money/100, money%100);
}
//...

このリンクを提供してくれた Vlad に感謝します: What Every Computer Scientist Should Know About Floating-Point Arithmetic

于 2012-12-28T20:35:14.837 に答える
2

すべてのプログラマーが浮動小数点数について知っておくべきこと

素晴らしい浮動小数点数の世界へようこそ。0.1 と 0.01 は正確には表現されません (1/3 に最終的な 10 進表現がないのと同様)。私の推測では、最初の例の 0.10 は実際には 0.099999999998 または同様の数値です。したがって、0.099999999998 < 0.10 となり、比較は失敗します。

これには 2 つの解決策があります。

  • 浮動小数点数を使用せず、固定小数点表現 (セント数) を使用します。
  • イプシロン比較を使用します。いえif (x - 0.005 >= 10) { ...

個人的には最初の解決策をお勧めします。

: 以下のテキストでは、簡単にするために 10 進数の 3 SF 番号と 1/3 と 1/999 を使用します (3 本指エイリアンのコイン計算は地球に委託されました)。

あなたのコードはおおよそ次のとおりです。

while (money >= 1/3) {
    money -= 1/3;
    coins++;
}
while (money >= 1/999) {
    money -= 1/999;
    coins++;
}

コンパイル後は次のようになります。

while (money >= 0.333) {
    money -= 0.333;
    coins++;
}
while (money >= 0.001) {
    money -= 0.001;
    coins++;
}

10 などの大きな数値を入力してみましょう。最初の実行後は次のようになります。

money = 10 - 0.333 = 9.667 ≈ 9.67
money = 9.67 - 0.333 = 9.334 ≈ 9.33
money = 9.00 - 0.333 = 8.667 ≈ 8.66
...
money = 1.00 - 0.333 = 0.667 ≈ 0.667
money = 0.667 - 0.333 = 0.333 ≈ 0.334
money = 0.334 - 0.333 = 0.001 ≈ 0.001
// Next loop
money = 0.001 - 0.001 = 0.000

アップス - 1 コインを数えすぎました。

于 2012-12-28T20:37:16.777 に答える
1

浮動小数点数は正確ではないためです。

通常、これらの種類の数値は、IEEE-754浮動小数点形式を使用して表されます。これは一種のバイナリ エンコーディングです。しかし、すべての分数/有理数/実数を 2 進数で表現できるわけではないため、1.85 が実際には 1.8499274 または 1.85010374 などである場合があります。

==そのため、 and!=演算子による比較に頼るべきではありません。代わりに、2 つの数値が互いに十分に近いかどうかを確認する必要があります。

const float eps = 1.0e-5;
if (abs(number1 - number2) < eps) {
    // let's pretend they're equal
} else {
    // they aren't equal
}

詳細については、この論文を読むことをお勧めします。この論文ではすべてが詳細に説明されているため、浮動小数点数をより深く理解できます。

于 2012-12-28T20:35:29.430 に答える
0

floatあなたの問題はすべて、バイナリ浮動小数点、つまりorの使用に起因しますdouble。あなたの質問の数字の多くは、バイナリを使用して正確に表現できません。2 進浮動小数点で正確に表現できる数値は、k/2^n の形式です。ここで、k は整数で、n は負でない整数です。

したがって、これらの値は正確には表現できません。

0.1
0.05
0.01
2.1
1.85
1.6
1.35
1.1
0.85
0.6
0.35

等々。怖いですよね?!

使用している数値を正確に表すことができないため、丸め誤差が発生する可能性があります。そのため、プログラムは希望どおりに動作しません。

浮動小数点演算に関する標準的なリファレンスは、What Every Computer Scientist Should Know About Floating-Point Arithmeticです。

この優れた著作は、基本的なレベルで浮動小数点演算をカバーしています。2 進浮動小数点に限定されません。また、10 進法を使用した表現も考慮されます。そして、これがあなたの問題を解決する本当の鍵です。算術を正確にするには、2 進表現ではなく 10 進表現を使用する必要があります。これを行うと、通貨の値を正確に表すことができます。

残念ながら、一般的な C 実装には、10 進浮動小数点または固定小数点データ型が付属していません。したがって、独自のロールを作成するか、サードパーティのライブラリを見つける必要があります。

非常に単純な解決策として、小数点以下 2 桁までしか表現する必要がないと仮定すると、固定小数点 10 進表現を使用できます。値を変数に格納intし、 の暗黙的な乗法シフトを仮定します100

于 2012-12-28T21:36:26.037 に答える