Integer overflow in greedy coin counting - c

#include <stdio.h>
#include <cs50.h>
#include <math.h>
int main (void) {
printf ("Enter amount: ");
float amount = GetFloat();
int coins = 0;
while (amount != 0) {
if (fmod(amount, 0.25) == 0) {
amount = amount - 0.25;
coins += 1;
}
else if (fmod(amount, 0.10) == 0) {
amount = amount - 0.10;
coins += 1;
}
else if (fmod(amount, 0.05) == 0) {
amount = amount - 0.05;
coins += 1;
}
else {
amount = amount - 0.01;
coins += 1;
}
}
printf ("Coins : %d\n", coins);
}
I'm trying to implement a small greedy algorithm, in which a user inputs an amount of money ( Ex: 9.25 ) and we output the least amount of coins that it takes for us to exchange it in change( 25 cents, 10 cents, 5 cents and 1 cent only).
This algorithm works with int amounts like 10 or 20 and with amounts that only requires the program to use the 25 cents coins.
If I try an amount like 9.10 or 9.01, I get a runtime error, signed integer overflow. I understand what it means, but I don't understand how can the value of coins go so high all of a sudden.

As Danial Tran said it is better to use int when you do logical operations. Please read Why not use Double or Float to represent currency? Also you can avoid while loop.
#include <stdio.h>
#include <cs50.h>
#include <math.h>
int main (void) {
printf ("Enter amount: ");
//float amount = GetFloat(); //Please refer to chqrlie's comments below.
double amount = 0.0;
scanf("%lf",&amount); //I don't know GetFloat() equivalent for double. So using scanf().
long long int amountInt = (long long int) (amount * 100.0);
int coins = 0;
if (25 <= amountInt) {
coins += (amountInt/25);
amountInt = amountInt % 25;
}
if (10 <= amountInt) {
coins += (amountInt/10);
amountInt = amountInt % 10;
}
if (5 <= amountInt) {
coins += (amountInt/5);
amountInt = amountInt % 5;
}
if (1 <= amountInt) {
coins += amountInt;
}
printf ("Coins : %d\n", coins);
}

It seems that no one has answered the particular question
If i try an amount like 9.10 or 9.01, i get a runtime error,signed integer overflow, i understand what it means, but i don't understand how can the value of coins go so high all of a sudden.
so for the sake of completeness, partially as an exercise:
You can find the reason using a debugger. I copied your code and found that it runs for very long. Interrupting it a moment after start revealed that the code freezes repeating this check:
if (fmod(amount, 0.25) == 0) {
amount = amount - 0.25;
coins += 1;
}
At the moment of the interruption amount was about minus 1.2 million and coins almost 5 million. This clearly shows one thing you failed to check: negativity. It can happen easily with floats that you miss the exact zero, as the others have correctly reasoned, no need to repeat that. But if that happens, your program should get worried the moment amount gets negative. Otherwise, under the right conditions, it may as well keep subtracting its way towards negative infinity, and yes, integer overflow will happen in coins.

This is because the computer represent the decimals in binary (power of 2)
For 0.25, the computer can represent it properly in binary. This is because we can obtain 0.25 exactly using power of 2's.
However for 0.10 ( or other denominations mentioned ), they cannot be expressed exactly in powers of 2.
Suppose you try to obtain 0.1 using power of 2's , you won't be able to obtain it exactly. You can just go near it.
0.1 = 0.0625 ( 2^4 ) + 0.03125 ( 2^5 ) + 0.00390625 ( 2^-8) +...
You will approach 0.1 , but you'll never reach 0.1 exactly.
Float, double everyone has a fixed number of bits to represent a decimal. So it will keep only those many bits, whose sum would be slightly less than 0.1
If you want to follow the same approach, you can have 2 possible solutions :-
Use (amount > 0) instead of (amount != 0), or
Use currencies which can be expressed easily in powers of 2 e.g. 0.25, 0.125 , 0.375 , 0.06125.

Your algorithm is not correct:
For example with 30 cents you can exchange to: 25+5 (2 coins) with your algorithm it would be 10+10+10 (3 coins). So greedy means why it's greater than 25 cents then exchange to 25 cents first.
while (amount != 0) {
if (amount >= 0.25) {
amount = amount - 0.25;
coins += 1;
}
else if (amount >= 0.10) {
amount = amount - 0.10;
coins += 1;
}
else if (amount >= 0.05) {
amount = amount - 0.05;
coins += 1;
}
else {
amount = amount - 0.01;
coins += 1;
}
}
If you want to do that way.
Better way:
int coinTypes[] = {0.25, 0.1, 0.05, 0.01};
for (int i = 0; i < 4; i++) {
coins += floor(amount / coinTypes[i];
amount -= coins * coinsTypes[i];
}

The main problem with your algorithm is invalid usage of fmod function. According to definition the result of fmod(num, denom) is
fmod = num - floor(num/denom) * denom
where floor(num/denom) is integer.
Thus, fmod(9.10, 0.25) == 0.1, fmod(9.10, 0.10) == 0.1.
In addition, the manipulation with floating point number rarely gives exact results. so amount is never 0.

You cannot compute this with the float type. Amounts that are not exact multiples of 0.25 cannot be represented exactly in either the float or the double type.
You can fix this problem by computing an exact number of cents and dispatch it using integer arithmetics:
#include <stdio.h>
#include <math.h>
void print_coins(int coins, const char *singular, const char *plural) {
if (coins > 0)
printf(" %d %s", coins, coins > 1 ? plural : singular);
}
int main(void) {
for (;;) {
double amount;
int amountInt, coins, quarters, dimes, nickels, pennies;
printf("Enter amount: ");
if (scanf("%lf", &amount) != 1 || amount <= 0)
break;
amountInt = (int)(amount * 100.0 + 0.5);
quarters = amountInt / 25;
amountInt %= 25;
dimes = amountInt / 10;
amountInt %= 10;
nickels = amountInt / 5;
amountInt %= 5;
pennies = amountInt;
amountInt = 0;
coins = quarters + dimes + nickels + pennies;
printf("coins returned: %d:", coins);
print_coins(quarters, "quarter", "quarters");
print_coins(dimes, "dime", "dimes");
print_coins(nickels, "nickel", "nickels");
print_coins(pennies, "penny", "pennies");
printf("\n");
}
return 0;
}
Notes:
Using float without the + 0.5, the change is incorrect for as little as 100.10: one penny short.
Using float, the change is incorrect for 1000000.10: 3 extra pennies.
To handle amounts above 20 million dollars, you need a larger integral type such as long long int. With that, you can handle amounts exceeding the US national debt.

Related

I came up with my own version of "Cash" problem from CS50x, can I optimize it? [duplicate]

This question already has answers here:
Cannot figure out how to properly increment a variable inside of a while loop, C
(2 answers)
Closed 2 years ago.
So basically, cash is a problem where you need to code a program that counts how many coins there is in a set sum.
For instance : for 0.41$, the minimum coins you can owe is 4 (1 quarter, 1 dime, 1 nickel, 1 penny).
The exercise will ask you just to code something that will answer : 4 for the entry "0.41"
I changed a bit the program so it answers for 4.2$ for example :
"16 quarter(s)
2 dime(s)
0 nickel(s)
0 penny(ies)" and was wondering if there is a way to optimize the code and make it better ?
Here is the code :
#include <cs50.h>
#include <math.h>
int main(void)
{
//Prompt user for an amount of change
float n;
do
{
n = get_float("How much change is owed for : (in $)\n");
//Verify if the float is positive
}
while (n < 0);
//Multiply 'n' by 100 to avoid float imprecisions
int cent = round(n * 100);
// Initialize the number of quarters (zero initially)
int quarters = 0;
while (cent >= 25)
{
quarters++;
cent = cent - 25;
}
// Initialize and calculate the number of dimes (zero initially)
int dimes = 0;
while (cent >= 10)
{
dimes++;
cent = cent - 10;
}
// Initialize and calculate the number of nickels (zero initially)
int nickels = 0;
while (cent >= 5)
{
nickels++;
cent = cent - 5;
}
// Initialize and calculate the number of pennies (zero initially)
int pennies = 0;
while (cent >= 1)
{
pennies++;
cent = cent - 1;
}
//Print the results
printf("%i quarter(s)\n", quarters);
printf("%i dime(s)\n", dimes);
printf("%i nickel(s)\n", nickels);
printf("%i penny(ies)\n", pennies);
}
As suggested by #MikeCAT, it'd be great to change it such that you use division and the modulo operator. This way, you'll avoid your while loop saving a few operations. Further, as #Weather Vane suggested, you can also have an array of multiple coin values and a singular loop. It'd also be great to print the coins depending on what all denominations there are. It would also be nice to determine the number of denominations (n_denomintations) at runtime. The code for the same would look like:
int cent = round(n * 100);
int denominations[n_denominations] = {25, 10, 5, 1}; // coins in descending order, alternatively ascending order and iterate in opposite
int n_denominations = sizeof(prices) / sizeof(prices[0]); // how many denominations of coins
int coins[n_denominations];
for (int i = 0; i < n_denomintations; i++) {
coins[i] = cent / 25; // implicitly typecasts to int, meaning that quarters will get whatever value cent / 25 is, rounded down
cent = cent % denominations[i]; // will give whatever amount remains after removing quarters
}
for (int i = 0; i < n_denominations; i++) {
printf("%i cent coins: %i\n", denomintations[i], coins[i]);
}

Greedy algorithms: "expression result unused"

I am trying to do a greedy algorithm for CS50, but for some reason I keep getting errors saying "expression result unused" when trying to divide the total sum of cents by 25, 10, 5, etc. Would someone be able to pinpoint where I am going wrong? Thank you so much in advance.
#include <cs50.h>
#include <stdio.h>
#include <math.h>
int main(void)
{
float Change;
int quarters = 25;
int dimes = 10;
int nickels = 5;
int pennies = 1;
int count = 0;
do
{
Change = get_float("Change: ");
}
while (Change < 0);
int cents = round(Change * 100);
while (cents % 25 >= 25)
{
cents/25;
count = count + 1;
}
while (cents % 10 >= 10)
{
cents/10;
count = count + 1;
}
while (cents % 5 >= 5)
{
cents/5;
count = count + 1;
}
while (cents % 1 >= 1)
{
cents/1;
count = count + 1;
}
printf("%d coins", count);
}
Wrong compare
The following is never true. cents % 25, the remainder after division, is always less than 25.
while (cents % 25 >= 25)
Useless code
The below divides by 25 then discards the quotient. #Some programmer dude
cents/25;
Instead in the 25, 10, 5, 1 places
// while (cents % 25 >= 25)
while (cents >= 25)
// cents/25;
cents -= 25;
More efficient code possible. Hint: while loops not needed.
Good to convert input to integers.
Deeper:
int cents = round(Change * 100); produces a float product from Change * 100, then converts to double in calling round(double). Lastly it converts the results to int as part of the assignment.
Could have used roundf(float) and skip the double conversion.
For learner code this is a good start. Note that Change * 100 can causes a rounding error which would show itself in select cases near a half cent. Suggest round(Change * 100.0)
Perhaps use long lround(double x) to expiate the final conversion from double to an integer.
Note that excessively large input causes overflow and undefined behavior.
Just as the error message is telling you: The value you are calculating cents/n isn't going anywhere. I think you are trying to do cents = cents/n.
Also, yourwhile loops are checking the value of cents but you are incrementing the variable count. So you will never exit the loop.

New at programming, off by a penny? in C [duplicate]

This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 5 years ago.
Hi so I'm struggling to figure out what's wrong with my code. I'm really new at this so bear with me. Comments might be a little messy atm.
#include <stdio.h>
int main(void)
{
double cost, gstCost, newCost; // initial cost, cost of gst by itself, new cost after gst
int loonies, quarters, dimes, nickels, pennies; // used for quantity of coins
float loonreq, quartreq, dimereq, nickreq, penreq; // cost required after deduction of the corresponding coin
printf("Please enter the amount to be paid: $");
scanf("%lf", &cost); // scanf allows to input an amount as a double ("%lf")
gstCost = cost * .13 + .005; // .13 for gst and add 0.005 to round up or down
printf("GST: %.2lf\n", gstCost);
newCost = cost + gstCost;
printf("Balance owing: $%.2lf\n", newCost); // original cost + gst cost
loonies = newCost; // loonies will output as integer when cost has decimals
printf("Loonies required: %d", loonies);
if (loonies == 0) // == used to compare equality
loonies = 1; // if loonies = 0, loonreq will be infinite causing code to crash and stop
loonreq = (float)((int)(100 * newCost) % (100 * loonies)) / 100; // casting int and * 100 on float values for modulo since it can only calculate for integer value
printf(", balance owing $%.2f\n", loonreq); // %.2f shows a float with 2 decimal places
quarters = 100 * loonreq / 25; // 100*loonreq allows the code to find how many quarters by dividing by 25
printf("Quarters required: %d", quarters);
if (quarters == 0)
quarters = 1;
quartreq = (float)((int)(100 * loonreq) % (int)(100 * (quarters * .25))) / 100;
printf(", balance owing $%.2f\n", quartreq);
dimes = 100 * quartreq / 10;
printf("Dimes required: %d", dimes);
if (dimes == 0)
dimes = 1;
dimereq = (float)((int)(100 * quartreq) % (int)(100 * (dimes * .10))) / 100;
printf(", balance owing $%.2f\n", dimereq);
nickels = 100 * dimereq / 5;
printf("Nickels required: %d", nickels);
if (nickels == 0)
nickels = 1;
nickreq = (float)((int)(100 * dimereq) % (int)(100 * (nickels * .05))) / 100;
printf(", balance owing $%.2f\n", nickreq);
pennies = 100 * nickreq / 1;
printf("Pennies required: %d", pennies);
if (pennies == 0)
pennies = 1;
penreq = (float)((int)(100 * nickreq) % (int)(100 * (pennies * .01))) / 100;
printf(", balance owing $%.2f\n", penreq);
return 0;
}
When I run this code on Visual Studio, I get the correct outputs for certain inputs like 8.68 but when I submit this file through PuTTY for my professor, it tests it for me and I get an incorrect balance (a penny off) after dimes deducted. When I put in a value like 76.54, I'm a penny off after the deduction of loonies. Inputting a value like 76.76, I get the correct output. I don't exactly know what's happening. My professor looked at my code and said it's because of type changes? and recommended that I use this method instead, example
dimes = (balance / 100) / 0.1;
balance = (int)(balance) % 10;
I'm not sure how to use this method though.
Edit: I need to use the modulus operator and casting for this assignment. gcc is used when I submit on PuTTy.
The rounding logic is not giving the correct answer. Say we have an input 123. The GST should be 15.99. However your code will give 16.00. To fix this try replacing gstCost = cost * .13 + .005; with gstCost = (round(cost * .13 * 100) / 100);
Don't use float/double at all for money. Computers are binary and use base 2 math. Not all decimal values (base 10) can be represented precisely in base 2 floating point, causing rounding errors. Do your math with int (or long long) representing pennies and it will be accurate up to the max value of a 32-bit signed int (millions) or 64-bit signed long long (quadrillions).
(float)((int)(100 * quartreq) % (int)(100 * (dimes * .10))) / 100;
I'm sure that changing types like float to int and vise versa can lose a some data throught the parsing part.
example: you have a double variable set as 1.2 when you parse it to int it become 1 and not 1.2 and it won't become 1.2 when you parse it back to double.
Do your calculations with the float type and if you want to change data type only parse it in the final result.

Greedy Program Running Forever

I recently developed a simple program designed to take a certain amount of money (in dollars) and determine the least number of coins necessary to fulfill that requirement.
#include <stdio.h>
#include <cs50.h>
int main(void)
{
// prompts for change and assigns variable owed_change that value
float owed_change = -1;
while (owed_change < 0 )
{
printf("How much change is owed?\n");
owed_change = GetFloat();
}
// sets the varialble n_coins to 0
int n_coins = 0;
// repeats until no more change is owed
while (owed_change != 0)
{
/* checks for the biggest coin that can be used and increases
the number of coins by 1 */
if (owed_change >= .25)
{
owed_change -= .25;
n_coins++;
}
else if (owed_change >= .10)
{
owed_change -= .10;
n_coins++;
}
else if (owed_change >= .05)
{
owed_change -= .05;
n_coins++;
}
else
{
owed_change -= .01;
n_coins++;
}
}
printf("%d\n", n_coins);
}
The program works for multiples of .25 but runs forever for any other number. From testing, I have found out that it has something to do with the variable owed_change being subtracted from and coming to the result of -0, which satisfies owed_change != 0. However, from research, I have found out that -0 as a floating point should act as +0. If so, what else have I done wrong?
It would be better, in your case, to work with money as cents and multiply all your values by 100. The reason for this is that floats are not exact values; that would be the reason why your code works for floating point values like 0.25, but not for smaller floating point numbers like 0.1, 0.05, and 0.01. For your purpose, you would be better off using an int value.
Instead of:
0.25$, use 25 cents
0.10$, use 10 cents
0.05$, use 5 cents
0.01$, use 1 cent
Change the data type of owed_change to int from float after making the above changes. That should resolve your problem.

Counting Change Program - C

I ran into a peculiar problem when trying to write a program for counting out change for a user. The user would enter in an amount -- in dollars and cents -- and the program would compute how many bills and coins the user would receive.
I have issues with counting out the coins portion, as I am not calculating the amount I expect to be calculating.
For example, the user would input 123.45 so I would pull the 123 out and compute the dollars portion, and then pull the .45 and compute the coins portion.
I feel like there is an issue with casting a floating-point number to an integer, but I'm not sure. If I enter .45 and then try and print it, the program would output .449997 instead.
What's going on here?
#include <stdio.h>
void giveChange(float);
void countCents(float);
int main()
{
float amount;
printf("Enter in an amount (in dollars and cents): ");
scanf("%f", &amount);
giveChange(amount);
getchar();
return 0;
}
void giveChange(float amount)
{
int newAmount = (int)amount;
int hundreds, fifties, tens, fives, ones;
float cents = amount - newAmount;
hundreds = newAmount / 100;
newAmount %= 100;
fifties = newAmount / 50;
newAmount %= 50;
tens = newAmount / 10;
newAmount %= 10;
fives = newAmount / 5;
newAmount %= 5;
printf("\nHundreds: %i\nFifties: %i\nTens: %i\nFives: %i\nOnes: %i\n", hundreds, fifties, tens, fives, newAmount);
countCents(cents);
return;
}
void countCents(float cents)
{
int newCents = cents * 10;
int quarters, dimes, nickels, pennies;
quarters = newCents / 25;
newCents %= 25;
dimes = newCents / 10;
newCents %= 10;
nickels = newCents / 5;
newCents %= 5;
printf("\nQuarters: %i\nDimes: %i\nNickels: %i\nPennies: %i\n", quarters, nickels, dimes, newCents);
return;
}
You are using floating-point data types to represent fixed-point calculations. Floating-point arithmetic is subject to a number of rounding errors such as those you've seen.
Instead of using float or double to represent dollars and fractions, use a plain int representing the total amount of cents. For example, instead of using float value = 4.25; for $4.25, use int value = 425; (425 cents) instead. Then, value/100 (== 4) is the number of dollars (note: integer division is truncated), and value%100 (== 25) is the number of pennies.
(This is called 'fixed-point' since the decimal point position is fixed in the representation (in this case, it is always after exactly two decimal digits). Fixed-point arithmetic is basically integer arithmetic with post-calculation corrections.)
You can't represent change with a float. You need to treat money as an integral number of cents (or tenths of a cent, hundredths of a cent, &ct depending on application).
Try something like this.
#include <stdio.h>
int main (void)
{
int dollars;
int cents;
scanf ("%d.%d", &dollars, &cents);
printf ("%d dollars, %d cents\n", dollars, cents);
return 0;
}
Long story short floats do not work like ints and thus you cannot express precise numbers with them.
See here.
Try multiplying the input by 100 and then using %100!

Resources