首页 文章

C Double到Int转换并发症

提问于
浏览
1

我正在尝试在C中创建一个收银机应用程序,它要求一个项目的价格和来自客户的付款金额,然后它显示美元,四分之一,硬币和便士的变化:

#include <iostream>
using namespace std;

void printChange(int&, int&, int&, int&, int&);
void findCoins(int&, int&, int&, int&, int&);


int main()
{
    double price;
    double payment;
    char answer = 'y';
    int dollars, quarters, dimes, nickels, pennies;
    while(answer == 'y')
    {
        cout<<"Enter price of an item: "<<endl;
        cin>>price;
        cout<<"Enter payment from customer: "<<endl;
        cin>>payment;
        double change = payment - price;
        dollars = change;               //use implicit conversion
        change = change * 100;          //multiplication

        int coins = change - dollars * 100;
        findCoins(coins, quarters, dimes, nickels, pennies);

        printChange(dollars, quarters, dimes, nickels, pennies);
        cout<<"Do you have another transaction?";
        cin>>answer;
    }
    cout<<"Thanks for shopping at Albertsons!"<<endl;
    return 0;
}

void printChange(int& dol, int& q, int& d, int& n, int& p)
{
    cout<<"dollars "<<dol<<endl;
    cout<<"quarters: "<<q<<endl;
    cout<<"dimes:  "<<d<<endl;
    cout<<"nickels: "<<n<<endl;
    cout<<"pennies: "<<p<<endl;
}
void findCoins(int& coins, int& quarters, int& dimes,
        int& nickels, int& pennies)
{
    quarters = coins/25;                //use implicit conversion
    dimes = coins % 25 / 10;            //use remainder division
    nickels = coins % 25 % 10 / 5;
    pennies = coins % 25 % 10 % 5;
}

这里的问题是我需要将变化(这是一个双倍)转换成硬币(这是一个整数),以便我可以使用模数来确定出纳员欠客户多少季度,硬币,镍币和硬币,无论如何我接近问题的角度我一直得到同样的错误 - 原始的双倍值减去1(在某些情况下) .

例如 .

Enter price of an item: 
10
Enter payment from customer: 
10.69
dollars 0
quarters: 2
dimes:  1
nickels: 1
pennies: 3

相比:

Enter price of an item: 
1.26
Enter payment from customer: 
5.00
dollars 3
quarters: 2
dimes:  2
nickels: 0
pennies: 4

注意:我理解我可以使用fmod()而不是模数来表示双值,但即使将硬币更改为double,我仍然会遇到相同类型的截断错误,特别是在以下场景中:

Enter price of an item: 
45
Enter payment from customer: 
47.47
dollars 2
quarters: 1
dimes:  2
nickels: 0
pennies: 1

请注意我是如何减少一分钱(与第一个例子相同)?我对C比较陌生,但我有超过一年的Java经验,从double转换为int时我从未遇到过这种奇怪的截断错误(我知道在某些情况下会丢失小数点,但为什么在多个实例中将整个double值减1?

我的代码有什么变化,具体来说,你会建议防止这种烦人的截断错误吗?

4 回答

  • 0

    Round 到整数 .


    考虑 payment, price, change 中的值:10.69,10.00,0.69 .

    典型的 double 可以精确地表示大约2 ** 64个不同的数字 . 由于大多数 double 使用二进制表示,因此数字的精确十进制值(如1.23)不在该集合中 . 在这种情况下, cin>>price 会将文本转换为最接近的 double .

    text   exact double value
    10.69: 10.6899999999999995026200849679298698902130126953125  
    10.00: 10.00  
     0.69:  0.689999999999999946709294817992486059665679931640625
    

    下面发生密钥失败 . 让我们假设 change - dollars * 100 导致 68.99999999999998578914528479799628257751465 . 转换为 int coins 的值是 68 而不是希望 69 . 该值未舍入到最近,但截断为零 .

    int coins = change - dollars * 100;
    

    处理这个问题的方法很多,都有利有弊 .

    • 将FP货币转换为最低财务单位的最接近的整数金额 . 然后继续进行仅使用整数的数学运算 . (con:在需要时容易忘记执行舍入)
    const double money_base_unit = 0.01;
    double change = payment - price;
    coins = round(change/money_base_unit);
    
    • 以类似 long long 的整数货币开头,就像一分钱一样基本单位而言 . (con:溢出问题,基本单位的一小部分货币 Value ,利率/利率计算等)

    • 使用十进制浮点类型 . (通常不可用或仿真速度慢)

    • 使用定点类型 . (更少可用)

    • 使用专门为金钱设计的课程,其中包含上述其中一项以处理财务细微差别 .


    对于OP,简单的解决方案是将这些货币 Value 适当地舍入到整数便士 .

  • 0

    我的代码有什么变化,具体来说,你会建议防止这种烦人的截断错误吗?

    永远,永远,永远,永远,永远不会因任何原因用任何编程语言记录具有浮点值的货币值 .

    如果您的代码与加油站价格一起使用时感谢我,并在需要以文本方式显示它们时将它们乘以美元金额 . 编写一个自定义 Currency 类,它将自动执行这些转换,并将金额的内部记录保持为一个整数定点数,但它将转换为浮点数,仅在需要时显示 .

  • 0

    要添加Xirema的答案,如果您需要编写货币类时可能会考虑的事项示例,可以使用code review上的帖子作为参考 .

    我可能会把这个特定的问题砍掉 change - dollars * 100 ,默认情况下整数转换不会舍入 .

  • 3

    @xirema是对的,错误主要在这一行

    int coins = change - dollars * 100;
    

    虽然它的合法性,并且变化= 69和美元= 0,但之后硬币将是68 .

    不要这样做,最好使用int .

    如果你现在要解决这个问题,这是做坏事的坏方法 .

    float dcoins = change - (dollars * 100);
        int coins = dcoins;
    

相关问题