Reputation: 25
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
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.
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);
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.)
Use decimal floating point type. (Not commonly available or slow in emulation)
Use fixed point type. (Even less available)
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
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
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
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