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()等待回车键离开主菜单并accept()仅读取 y/n/e,返回truey、falsen 并离开到 e 上的菜单。

控制变量的冗长部分是我检查数字是否不比 0.01 更精确的解决方案。可悲的是,由于strtod().

我的问题是它strtod()真的不起作用!即使使用真正的医学数字,远离下溢或溢出,返回的值也与输入不匹配。一些例子:

    输入您要存入的金额。
        金额:1234.45

    GOT 1234.45000000000004547474

    输入您要存入的金额。
        金额:0.999

    得到 0.99899999999999999911

这并非不可能是我的错,但是在使用这段代码几个小时后,我仍然无法想出一个可行的解决方案,这就是为什么我寻求你的帮助,Stack Overflow 的内部人员。

有什么办法可以解决阅读问题strtod()吗?如果没有,是否有另一种方法来获取该输入,让我检查所有需要检查的内容?

编辑:请注意!

如果我还没有说过,我的导师不是最容易相处的人,我现在就这样做。
他强迫我们使用 DOUBLE 格式来保存帐户中存储的 BALANCE。我知道这一点是因为我的一位同事因为使用两整数美元分的结构而拒绝了他的程序。

我非常感谢你在这里的帮助,那么你能以某种方式解决这个问题吗?我必须使用双类型来存钱。我还必须检查输入中是否没有字母(scanf()设置 on%Lf只会从末尾删除所有非数字),必须检查输入是否比 0.01 更精确,必须接受 xxx.xx 结构输入。有什么建议么?

4

5 回答 5

8

用于strtol读取美元并再次(或只是您自己的微不足道的函数)读取美分,然后将它们组合为cents += 100*dollars;不要将浮点用于货币。曾经。

于 2015-06-02T20:10:08.527 回答
2

IEEE 浮点数旨在保存很大范围内的近似值,而不是精确值。它们不能精确地表示一些非常简单的值,例如0.1,但它们可以精确地表示1.0, 0.5, 0.25, 0.125, ...。它们还可以表示2.0, 4.0, 8.0,16.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 的幂的数字时,总是有可能发生这种情况.)

您可以做的另一个选择是创建一个包含两个整数(美元部分和小数部分)的“货币”结构并将其传递。不过,您仍然需要处理将货币字符串拆分为这些部分的代码。

于 2015-06-02T20:30:35.910 回答
0

如果使用双精度数的要求很严格,那么可能会使用浮点数的另一个属性:存储在浮点变量中的任意整数值比具有小数部分的数字更可能是精确的。

这个事实让我不禁要问:你有没有想过存储你的货币价值,使得 1234.45 美元等于 123445?

于 2015-06-02T21:18:01.003 回答