Pennies on change making program not adding up

I am in a intro C++ class. Our 4th group project has us making a program to calculate change from purchase price and tendered amount with outputs of the number and type of bills and coins. The problem my group is having is that with certain remainders the amount of pennies is one cent short. So if the amount due is $10.00 and the amount tendered is $10.78 then it gives the correct change due of 0.78 cents but when the amount of coins is returned its one cent off. We think we've narrowed down the problem to the $10 part of the bills calculation but have been stuck there. We did find and run other code examples from the internet and they all have the same issue. We found 5 different programs and they all had this problem. For clarity, entering an amount tendered of 20.78, 5.78, 1.78, and 0.78 all return the correct number of pennies. It is only the 10s place that has the problem (EX 10.78 returns only 2 pennies instead of 3). We broke into subgroups that came up with slightly different solutions but both groups are having the same problem. I have included both code sets.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  //Code from one subgroup

#include <iostream>
using namespace std;

void billCalc(int, int&, int&, int&, int&, int&);
void coinCalc(int, int&, int&, int&, int&);

int main()
{
	char repeat = 'y';
	
	while (repeat =='y'|| repeat =='Y')
	{
		int hundred, twenty, ten, five, one, quarter, dime, nickel, penny, dollars, cents;
		float purchased, tendered;
		
		cout << "Enter a purchase amount: $";
		cin >> purchased;
		
		while(purchased < 0)
		{
			cout << "Please enter positive purchase amount: $";
			cin >> purchased;
		}
		
		cout << "Enter amount tendered: $";
		cin >> tendered;
		
		while(tendered < purchased)
		{
			cout << "Please enter amount tendered more than purchase amount: $";
			cin >> tendered;
		}
		
		dollars = (tendered-purchased)/1;
		cents = ((tendered-purchased)-dollars)*100;
		
		billCalc(dollars, hundred, twenty, ten, five, one);
		coinCalc(cents, quarter, dime, nickel, penny);
		
		//magic 2 decimal
		cout.setf(ios::fixed);
		cout.setf(ios::showpoint);
		cout.precision(2);	
		
		cout << "Amount of purchase: $" << purchased << endl;
		cout << "Amount tendered: $" << tendered << endl;
		cout << "Change due: $" << (tendered-purchased) << endl;
		cout << "Bills:\n" << hundred << " - $100's " << twenty << " - $20's " << ten << " - $10's " << five << " - $5's " << one << " - $1's\n";
		cout << "Coins:\n" << quarter << " - quarter(s) " << dime << " - dime(s) " << nickel << " - nickel(s) " << penny << " - penny(ies)\n";
	
		cout << "Continue (y or n)? ";
		cin >> repeat;
	}	
	return 0;
}

void billCalc(int dollars, int& hundereds, int& twenties, int& tens, int& fives, int& ones)
{
	hundereds = dollars/100;
	twenties = dollars%100/20;
	tens = dollars%100%20/10;
	fives = dollars%100%20%10/5;
	ones = dollars%100%20%10%5;
}

void coinCalc(int cents, int& quarters, int& dimes, int& nickels, int& pennies)
{
	quarters = cents/25;
	dimes = cents%25/10;
	nickels = cents%25%10/5;
	pennies = cents%25%10%5;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//Code from other subgroup

#include <iostream>
#include <cmath>
using namespace std;

void inputs(double&, double&, double&, int&, int&);
void bills(int, int&, int&, int&, int&);
void coins(int, int&, int&, int&, int&);
void output(double, double, double, int, int, int, int, int, int, int, int);

int main()
{
	double purchase, tendered, change;
	int dollars, cents, twenties, tens, fives, ones, quarters, dimes, nickels, pennies;
	char repeat;

	do
	{
		inputs(purchase, tendered, change, dollars, cents);
		bills(dollars, twenties, tens, fives, ones);
		coins(cents, quarters, dimes, nickels, pennies);
		output(purchase, tendered, change, twenties, tens, fives, ones, quarters, dimes, nickels, pennies);
		
		cout << "Continue (y or n)? ";
		cin >>repeat;
  
}
	while(repeat =='y' || repeat =='Y'); 

	return 0;
}

void inputs(double &purchase, double &tendered, double &change, int &dollars, int &cents)
{
  
	cout << "\nEnter purchase amount: $" ;
	cin >> purchase;
  
	cout << "\nEnter amount tendered: $";
	cin >> tendered;
  
	if(tendered > purchase)
	{
	change = tendered - purchase;
	float dollar = floor(change);
	dollars = dollar;
	cents = (change - dollars)*100;
	}
}


void bills(int dollars, int &twenties, int &tens, int &fives, int &ones)
{
	twenties = dollars / 20;
	dollars %= 20;
  
	tens = dollars / 10;
	dollars %= 10;
  
	fives = dollars / 5;
	dollars %= 5;
  
	ones = dollars;
}

void coins(int cents, int &quarters, int &dimes, int &nickels, int &pennies)
{
	quarters = cents / 25;
	cents %= 25;
  
	dimes = cents / 10;
	cents %= 10;
  
	nickels = cents / 5;
	cents %= 5;
  
	pennies = cents;
}


void output(double purchase, double tendered, double change, int twenties, int tens,int fives,int ones, int quarters, int dimes,int nickels, int pennies)
{
	cout<<"Amount of purchase: $" << purchase <<endl;
	cout<<"Amount tendered: $" << tendered <<endl;
	cout<<"Change due: $" << change <<endl;
	cout <<"Bills:" <<endl;
	cout << twenties << " - $20's " << tens << " - $10's " << fives << " - $5's " << ones << " - $1's" <<endl;
	cout <<"Coins: " <<endl;
	cout<< quarters <<" - quarter(s) " << dimes << " - dime(s) " << nickels << " - nickel(s) " << pennies << " - penny(ies)" <<endl;
}

It appears you're getting burned by the nature of binary floating point values.

Similarly to how ⅓ as a decimal involves an infinitely long string of 3s after the decimal point, floats and doubles can't perfectly represent all decimal numbers. In many cases, there's going to be a little bit of error.

In some cases, that error can bump a value down to less than the number you wanted. Meaning, when the double gets truncated during its conversion to an integer, it gets rounded down, effectively.

IMO the best approach would be to read in a string and convert it into two integers: one before the decimal point, and one after. However, if you don't want to do that, another thing you could do is multiplying your doubles by 100, and then rounding them to the nearest integer. A common way to do this is do add 0.5 before doing the integer conversion, but if you have access to one of the std::round functions in <cmath>, that would be better.

-Albatross
Hello TaftBeach,

As Albatross has said and to expand on it a bit.

To store the number "0.3" in a double you might find that what is stored is "0.29999999999999999" or "0.03" as "0.02999999999999999". In a float this might be "0.3" as "0.300000012" and "0.03" as "0.0299999993". I have also seen a float store a decimal number higher than what was entered.

Not usually a problem when you only use 2 decimal places in your output, but does become a problem when used in a calculation especially a number that is used several times to arrive at your final result. This could be off by 1 or 2 pennies.

More often I have read replies that suggest using just 1 int to hold the amount. Where 1 = 1 penny, (USD) as the base, and 100 would be 100 pennies or 1 dollar. When you are ready for output it would be something like this:
std::cout << amount / 100 << '.' << amount % 100;. Saying that amount has the value of 1042 for $10.42 the output would be 10.42. You can fix the output any way that you like.

I would suggest using "double"s over "float"s for a more accurate, but not 100% accurate, value to work with, but you may still find that it is off now and then.

Andy
Thank you Albatorss! I added .5 after cents = (change - dollars)*100; so it is now cents = (change - dollars)*100 + .5 and cents = ((tendered-purchased)-dollars)*100+.5; in the other program. After testing this has resolved the problem and has not created any new ones. We haven't touched on creating strings too much, as we're only on chapter 5 of the book. I did some searching and I can see how it would be a much more powerful and elegant solution, but I don't quite grasp everything that is going on. You've been very helpful in not only assisting me in finding a quick solution, but also showing that there are better ways to solve the same problem. You have my (and my group's) gratitude.

-TaftBeach
Last edited on
Thank you Andy! I looked at the schedule and we will be diving into strings for the next assignment. I appreciate your expanded explanation. It helps me with understanding what I've been finding online. I still have so much to learn.
Topic archived. No new replies allowed.