Helgard Ferreira
Helgard Ferreira

Reputation: 25

C++ Double to Int Conversion Complication

I'm trying to create a cash register application in C++ which asks for the price of an item and the payment amount from the customer, where it then displays the change in dollars, quarters, dimes, and pennies:

#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;
}

The problem here is that I need to convert change (which is a double) into coins (which is an integer) so that I can use modulus in determining how many quarters, dimes, nickels, and pennies the cashier owes the customer and no matter what angle I approach the problem I keep getting the same error - the original double value is subtracted by 1 (in some cases).

E.g.

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

Compared to:

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

NOTE: I understand that I can utilize fmod() instead of modulus for double values, but I still ran into the same type of truncation error even when changing coins into a double, specifically in the following scenario:

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

Notice how I am one penny off (same issue as the first example)? I'm relatively new to C++, but I have over a year of experience in Java, and I have never run into this weird of a truncation error when converting from double to int (I understand that the decimal points are lost in certain cases, but why the subtraction of a whole double value by exactly 1 in multiple instances?)

What changes to my code, specifically, would you recommend as to prevent this annoying truncation error?

Upvotes: 1

Views: 1193

Answers (4)

chux
chux

Reputation: 154242

Round to whole numbers.


Consider what values are in payment, price, change: 10.69, 10.00, 0.69.

Typical double can represent exactly about 2**64 different numbers. As most double use a binary representation, exact decimal values of numbers like 1.23 are not in that set. In such cases, cin>>price will convert text to the closest double.

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

A key failing occurs with the below. Let us assume that change - dollars * 100 resulted in 68.99999999999998578914528479799628257751465. That value converted to int coins is 68 rather than the hoped for 69. The value was not rounded to nearest, but truncated toward zero.

int coins = change - dollars * 100;

To handle this are a number of approaches, all with pros and cons.

  1. Convert FP money to nearest whole number amounts of the lowest fiscal unit. And then proceed with math that only uses whole numbers. (con: easy to forget to perform rounding when needed)

    const double money_base_unit = 0.01;
    double change = payment - price;
    coins = round(change/money_base_unit);
    
  2. Begin with integer money with type long long in terms of some base unit like a penny. (con: trouble with overflow, money values of a fraction of the base unit, interest/rate calculations, etc.)

  3. Use decimal floating point type. (Not commonly available or slow in emulation)

  4. Use fixed point type. (Even less available)

  5. Use a class specifically design for money which wraps one of the above to handle financial nuances.


For OP, the simple solution is to round those money values properly to whole number of pennies.

Upvotes: 0

MoustafaS
MoustafaS

Reputation: 2031

@xirema is right, The error mainly is in this line

int coins = change - dollars * 100;

Although its legit, and change = 69 and dollars = 0, but after it coins will be 68.

Don't do this, and better use int.

IF you want to fix this for now, here is the bad way to do it.

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

Upvotes: 0

Assimilater
Assimilater

Reputation: 964

To add to Xirema's answer, if you need an example of things you might consider when writing a currency class you can use my post on code review for reference.

I'd probably chop this specific problem up to doing change - dollars * 100, where the integer cast doesn't round by default.

Upvotes: 0

Xirema
Xirema

Reputation: 20396

What changes to my code, specifically, would you recommend as to prevent this annoying truncation error?

Never, ever, ever, ever, ever*100, ever1700, record monetary values with floating point values, in any programming language, for any reason.

If you're working with US Dollars, record them in Integer (preferably 64-bit integer) cents (or tenths of a cent; you'll thank me when your code gets used with gas station prices) and multiply them up to dollar amounts when you need to textually display them. Write a custom Currency class which will perform these conversions automatically, and will keep the internal record of the amount as an integral fixed-point number but which will convert to floating point numbers for display only when needed.

Upvotes: 3

Related Questions