2

私は現在、私の大学での実験室演習用の銀行端末プログラムに取り組んでいます。

私の足を踏み外したのは、転送される金額のユーザー入力を受け取り、それがすべての要件に適合するかどうかを確認し、適合する場合はプログラムに提供された値を返す関数です。

私たちの家庭教師は、入力を保護するあらゆる手段に腹を立てているため、あらゆる種類の不一致についてエラーを呼び出す必要があります。価値が高すぎる?エラー。負またはゼロの値? エラー。0.01 よりも正確な数値は? エラー。入力に数字以外のドット以外の文字が含まれていますか? エラー。

そのため、私の機能は間違いなく過度に複雑ですが、それでも問題ありません。私を壁に押し上げるのは、atof()strtod()関数の両方が何らかの形で間違った方法で数値を読み取るという事実です。

long double take_amount()
{
    char input[21];
    bool success, correct;
    unsigned long int control1;
    long double amount, control, control2, control3;

    do
    {
        success = true, correct = true;
        printf("\t\tAmount:\t\t");
        success = scanf("%20[^ \t\n]c", input);
       __fpurge(stdin);

        correct = check(input, 'b');

        amount = strtod(input, NULL);

        printf("\n\tGOT %.20Lf\n", amount); ///

        control = amount * 100;
        control1 = (unsigned long int)floor(control);
        control2 = (long double) control1;
        control3 = control2 - control;    

        printf("\n\tGOT2 %.20Lf\n", control3);  ///

        if (control3 != 0)
        {
            if (control3 >= 1 || control3 <= -1)
            {
                printf("\n\t\tWe are sorry, but for the safety reasons it is impossible to transfer");
                printf("\n\t\tsuch a great amounts while online. If you really wish to finalize");
                printf("\n\t\tthis operation, please, visit the closest division of our bank.");
                printf("\n\t\tWe are sory for the inconvenience and wish you a pleasent day.");
                press_enter();

            }
            correct = false;
            printf("\nDAMN\n");     ///       

        }

        if (amount <= 0)
        {
            correct = false;
            printf("\nGOD DAMN\n");      ///
        }

        if(success == false || correct == false)
        {
            printf("\n\t\tInvalid input, please try again.\n\n");
        }
        else
        {
            printf("\t\t\t\t%.2Lf\n", amount);
            printf("\n\t\tIs it correct input? ((Y)es/(N)o/(E)xit)");
            if (accept())
            {
                break;
            }
            else
            {
                continue;
            }
            break;
        }
    }while (1);
    return amount;

ここで使用されている関数に関する限りcheck()、文字列に有効な文字 (数字とドット) のみが含まれているかどうかを確認し、press_enter()Enter キーを押してメイン メニューに移動するのを待機し、accept()y/n/e のみを読み取り、trueyに戻り、 falsenに戻り、終了します。 eのメニューへ。

制御変数の長い部分は、数値が 0.01 よりも正確でないかどうかを確認するための私の解決策です。悲しいことに、それは動作しませんstrtod()

私の問題は、それstrtod()が実際には機能しないことです!アンダーフローやオーバーフローにはほど遠い、本当に平凡な数値であっても、返される値は入力と一致しません。いくつかの例:

    入金したい金額を入力します。
        金額: 1234.45

    1234.45000000000004547474 を取得しました

    入金したい金額を入力します。
        金額: 0.999

    GOT 0.9989999999999999911

それが私のせいである可能性は低いですが、このコードを数時間使用した後でも、まだ機能する解決策を思い付くことができませんでした。

の読みを修正する方法はありますstrtod()か? そうでない場合、チェックする必要があるすべてをチェックできるように、その入力を取得する別の方法はありますか?

編集:注意してください!

私の家庭教師は一緒に仕事をするのが最も簡単な人ではないことをまだ述べていない場合は、今そうします.
彼は、アカウントに保存されている残高を保持するために、DOUBLE 形式を使用することを強制します。私の同僚の 1 人が、2 整数ドル - セント構造を使用したことでプログラムが拒否されたので、私はそれを知っています。

ここで助けていただければ幸いです。どうにかしてその問題を解決できますか? 私はお金を保管するためにダブルタイプを使わなければなりません。また、入力に文字が含まれていないかどうかを確認する必要があります(scanf()オンに設定する%Lfと、数字以外のすべてが末尾から削除されます)、入力が 0.01 よりも正確でないかどうかを確認する必要があります。入力。助言がありますか?

4

5 に答える 5

8

を使用strtolしてドルを読み取り、もう一度 (または独自の簡単な関数で) セントを読み取り、それらを次のように結合しますcents += 100*dollars;。通貨に浮動小数点を使用しないでください。これまで。

于 2015-06-02T20:10:08.527 に答える
2

IEEE 浮動小数点数は、正確な値ではなく、膨大な範囲におおよその値を保持するためのものです。などの非常に単純な値を正確に表すことはできませんが、、、、 ... を正確に0.1表すことはできます。、、、を表すこともできます。1.00.50.250.1252.04.08.016.0、 ... 正確に。これらのシリーズがどこまで進むかは、浮動小数点値が何ビットで表されるかに関連しています。私が示した例の値はすべて 2 の累乗であることに気付いたはずです。このことから、浮動小数点値も 2 の累乗の和で構成できることがわかります。これはほとんど正しいです。最小から最大までの範囲全体を整数型で表すことができるため、2 の値の合計は整数を表すのに適していますが、小数値の場合は事情が異なります。表現できる値の間には、特定の浮動小数点型 (32、64、80、または 128 ビットの浮動小数点変数) では表現できない値が多数あるため、これらを C ライブラリ関数 (またはソース コードからのコンパイラ) には、有効になる丸め規則があります。

正確な値を扱う必要がある場合は、これに注意する必要があります。通常、このタイプの精度が必要なお金については、このタイプの精度を表すことができるタイプに変換する必要があります。多くの言語では、これには個別の型がありますが、C では、値を整数型または整数で構成される構造体に入れることができます。

于 2015-06-02T20:28:26.723 に答える
1

分割統治

ユーザー入力をデータ検証から分離します。


ユーザー入力:

現在のコードはinput最初に検証せずに使用していますsuccess

success = scanf("%20[^ \t\n]c", input);
correct = check(input, 'b');
amount = strtod(input, NULL);  // Bad: `input` not known to have been successfully scanned.

その代わり:

char input[40]; // no need for tight buffer size
if (fgets(input, sizeof input, stdin) == NULL) Handle_EOF();
// remove potential trailing \n
input[strcspn(input, "\n")] = 0;

さまざまなテストで入力が有効かどうかの評価を開始します

char *endptr;
errno = 0;
double value = strtod(input, &endptr);
if (input == endptr) Handle_NothingScanned();
if (*endptr != '\0') Handle_GarbageAtEndOfInput();

// Use 1 of the 2: First allows input like 1e-1000 which underflows to 0.0
if (errno == ERANGE && fabs(value) > DBL_TRUE_MIN) Handle_Overflow();
if (errno == ERANGE) Handle_OverUnderflow();

// work with numbers in a reasonable range of 'double'
double max_range = pow(10.0, DBL_DIG - 2);
if (!(value >= -max_range && value <= max_range)) Handle_InfinityNotanumberOverflow();

// Test insures that the number entered is the closest possible `double` to xxxxxx.xx
// Method 1: Math
double rvalue = round(value*100.0)/100.0;
if (rvalue != value) Handle_NumberMorePreciseThan_0_01();
// Method 2: round trip: double --> string --> double
char buffer[DBL_DIG + 10];
sprintf(buffer, "%.2f", value);
if (value != strtod(buffer, 0)) Handle_NumberMorePreciseThan_0_01();

// Insure xxxx.xx format
if ((endptr - input) < 3 || endptr[-3] != '.') Handle_BadFormat();

GoodToUse(value);
于 2015-06-02T21:32:49.697 に答える
0

これは、数値を浮動小数点で表現する方法に関する既知の「問題」です。浮動小数点での 1234.45 の表現が入力されています。

これは、浮動小数点表現に変換すると、実際の正確なビット文字列が、それを格納するために使用されるスペースよりも長くなるという事実によるものです (2 の累乗ではない数を扱う場合、常にこれが発生する可能性があります)。 .)

実行できる別のオプションは、2 つの int (ドル部分と小数部分) を保持する 'Currency' 構造体を作成し、それを渡すことです。ただし、通貨文字列をこれらの部分に分割するためのコードを処理する必要があります。

于 2015-06-02T20:30:35.910 に答える
0

double 数を使用する要件が厳しい場合は、浮動小数点数の別のプロパティが役立つ場合があります。浮動小数点変数に格納された任意の整数値は、小数部分を持つ数値よりも正確である可能性が高くなります。

この事実から、私は次のように質問します。$1234.45 が 123445 になるように通貨の値を保存することを考えたことはありますか?

于 2015-06-02T21:18:01.003 に答える