C的strtod()返回错误的值

时间:2015-06-02 20:02:22

标签: c string double return-value strtol

我目前正在为我的大学的实验室练习制定银行终端计划。

让我摆脱困境的是一个假定用户输入金额的函数,检查它是否符合所有要求,如果是,则返回提供给程序的值。

我们的导师对保护输入的所有方法都很生气,所以我必须在任何不一致的情况下调用错误。价值太高了?错误。负值还是零值?错误。数字比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-press离开主菜单{{1}只读y / n / e,在y上返回accept(),在n上返回true,然后在e上留下菜单。

控制变量的冗长部分是我检查数字是否不精确到0.01的解决方案。遗憾的是,由于false,它无效。

我的问题是strtod()无效!即使有真正的医生数字,远离下溢或溢出,返回的值与输入不匹配。一些例子:

    Enter the amount you would like to deposit.
        Amount:     1234.45

    GOT 1234.45000000000004547474

    Enter the amount you would like to deposit.
        Amount:     0.999

    GOT 0.99899999999999999911

这不是我的错,但是在使用这段代码几个小时之后我仍然无法找到一个可行的解决方案,这就是为什么我要求你提供帮助,为Stack Overflow提供内部支持。

有没有办法解决strtod()的阅读?如果没有,是否有另一种方法可以让我检查所有需要检查的输入?

编辑:注意,请!

如果我还没有说过,我的导师不是最容易合作的人,我现在就这样做。
他强迫我们使用DOUBLE格式将BALANCE存储在帐户中。我知道这是因为我的一个同事让他的程序被拒绝使用两个美元的建筑。

我非常感谢你在这里提供帮助,所以你能以某种方式解决这个问题吗?我必须使用双重类型来存钱。我还要检查输入中是否没有字母(strtod()上设置的scanf()只会截断结尾处的所有非数字),还要检查输入是否不精确到0.01 ,必须接受输入的xxx.xx结构。有什么建议吗?

5 个答案:

答案 0 :(得分:9)

使用strtol来读取美元并再次(或只是你自己的琐碎函数)来读取美分,然后将它们合并为cents += 100*dollars;不要使用浮点数作为货币。如初。

答案 1 :(得分:2)

IEEE浮点数用于在很大范围内保持近似值,而不是精确值。它们不能精确地表示一些非常简单的值,例如0.1,但它们可以精确地表示1.00.50.250.125,....它们还可以精确地代表2.04.08.016.0,....这些系列走了多远与浮点值表示的位数有关。您应该已经注意到,我给出的所有示例值都是2的幂,从中你应该看到浮点值也可以由2的幂的和组成,这大多是正确的。对2的值求和可以很好地表示整数,因为从最小到最大的整个范围可以用整数类型表示,但是对于小数值,事物是不同的。在可以表示的值之间有许多值,这些值对于给定的浮点类型(32,64,80或128位浮点变量)是不可表达的,因此当这些值被C库函数(或通过源代码中的编译器)有一些舍入规则可以发挥作用。当数字由C程序写回时,会发生类似的事情。

当您需要处理精确值时,您必须注意这一点。对于通常需要这种精度的钱,您需要将其转换为可以表示此类精度的类型。在许多语言中,有不同的类型,但在C中,您可以将值放入整数类型或由整数组成的结构。

答案 2 :(得分: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);

答案 3 :(得分:0)

这是一个已知的问题&#34;关于数字如何以浮点表示。浮点数1234.45的表示形式是输入的内容。

这是因为当转换为浮点表示时,实际精确的位串比用于存储它的空间长(当处理不是2的幂的数时总是有机会发生这种情况。)

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

答案 4 :(得分:0)

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

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