user8287021 user8287021 -3 years ago 116
C Question

Printing values of float and int for a simple calculation (values not adding up)

Having trouble with float / int calculations for a program designed to convert a dollar value input into its possible constituent parts. For example, if you enter $ 1234.56, the program will output "1 $1000 bill, 2 $100 bills, 1 $20 bill, 1 $10 bill, 4 $4 dollar bills, 2 Quarters, 1 Nickel, and 1 cent."

The biggest issue comes up with calculating pennies. If the user inputs $ .03, the output will vary (at times, I get 3 cents; other times, I get 2 cents). I think I have solved the issue for now, but I would like to understand why I solved it and more importantly I'd like to address the underlying issue: somewhere along the calculation stream, the compiler appears to "shave off" value (e.g. .01 becomes .009999). Is it because I'm performing a recursive calculation on the same variable, namely "amount"? Is there a cleaner way to do what I am trying to do here? (I'm limited to switch / if selection statements, no use of functions, and no use of arrays.)

Link to full code here.

#include <stdio.h>

int main(){

float amount, bill_quarter_f, bill_dime_f, bill_nickel_f, bill_penny_f;
int bill_quarter, bill_dime, bill_nickel, bill_penny;

printf("Ex 5.15: Translating Decimal Dollar into Coins\n");
printf("==============================================\n\n");

printf("Enter your amount (e.g .98): $ ");
scanf("%f", &amount);

/************************************/
/* Calculation for greater than MAX */
/************************************/

if (amount >= 1)
printf("\nYou have too much money!\n\n");

/************************************/
/* Calculation for Coins */
/************************************/

else if(amount < 1 && amount > 0){
bill_quarter = amount / .25;
bill_quarter_f = amount / .25;
amount = ((bill_quarter_f - bill_quarter) * .25);


(bill_quarter >= 1 && bill_quarter < 4) ? (printf("You have %d quarters\n", bill_quarter)) : (printf(""));

bill_dime = amount / .10;
bill_dime_f = amount / .10;
amount = (bill_dime_f - bill_dime) * .10;


(bill_dime >= 1 && bill_dime <= 2) ? (printf("You have %d dimes\n", bill_dime)) : (printf(""));

bill_nickel = amount / .05;
bill_nickel_f = amount / .05;
amount = (bill_nickel_f - bill_nickel) * .05;


(bill_nickel >= 1 && bill_nickel < 2) ? (printf("You have %d nickel\n", bill_nickel)) : (printf(""));

bill_penny = amount / .01;
bill_penny_f = amount / .01;
amount = (bill_penny_f - bill_penny) * .01;

(bill_penny_f > 0 && bill_penny_f < 5) ? (printf("You have %.0f pennies\n", bill_penny_f)) : (printf(""));
}

else if (amount == 0)
printf("You have no money!\n");
else
printf("You are in debt!\n");

return 0;
}

Answer Source

Using binary floating-point for financial code has various pitfalls. OP's code exhibits some of those. Without addressing other other ways to represent money (each with their strengths and weaknesses), this answer presents ways to improve double usage with money as it applies to OP's code.

  1. Inexact representation of money

Recall a binary64 double can typically exactly represent about 264 different numbers. 0.10 is not one of them. So the following quotient will often result in a non-whole-number.

float amount;
...
amount / .10;

Assigning the quotient to an int results in a truncated value. Had the quotient been 1.9999..., bill_dime would take the value of 1.

int bill_dime;
...
bill_dime = amount / .10;
  1. No control over input that is not an exact multiple of 0.01.

What to do with user input like "0.125" --> amount = 0.125?

0.125 can be exactly represented as double, so there is no rounding issue on input. Yet code does not address how to round such a value or what to do with fraction of 0.01.

  1. No control over inputing rounded values.

What to do with user input like "0.10" --> that value can not be exactly represented? So a nearby amount = 0.10f results.

value takes on a float value 0.01f that is near 0.01 and may differ from a double 0.01. This difference may result in an unexpected result from amount / .10;. Mixing double and float contributed to this problem.


For financial code employing FP, do not use float - far too many issues.

For OP's learner usage of FP and financial code, recommend to convert the FP input to integers for change computation. As the conversion to long below certainly does not overflow, that concern of OF can be set aside.

printf("Enter your amount (e.g .98): $ ");
double amount;
if (scanf("%lf", &amount) != 1) Handle_NonNumericInput();

if (amount >= 1.0 || amount < 0.0)
  printf("\nYou have too much/too little money!\n\n");

Now move to integers

 else {
   // round ... to the nearest integer value, rounding halfway cases away from zero,
   long cents = lround(amount * 100.0);
   // 0 <= cents <= 100
   // Need to decide how to handle cents == 0 or cents == 100 here.

   bill_quarter = cents / 25;
   cents %= 25;

   if (bill_quarter) {
     printf("You have %d quarters\n", bill_quarter);
   }

  bill_dime = cents / 10;
  cents %= cents % 10;

   if (bill_dime) {
     printf("You have %d dimes\n", bill_dime);
   }

Minor

// "You have %d nickel"
"You have %d nickels"
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download