0

Edit:
I solved the issue by first multiplying the float value by 100, then rounding it with roundf() function, then casting it to an integer to be stored in an integer variable. I did the remaining operations with integer values from there on and everything worked. Even though the solution offered by @JacobBoertjes actually worked, my assignment requiered me to use get_float() from the cs50.h library, so I didn't implement it. Here's the final code:

// Get user input as a positive float value
float f_change;
do {
    printf("Change owed: ");
    f_change = get_float();
} while(f_change < 0);

// Round & cast
int int_change = (int) roundf(f_change * 100);

My program accepts an amount of money, say $4.20, and figures out the least amount of coins with which it can represent this value. For example, desired output from the program with $4.20 as an input would be: 16 quarters ($4.00), 2 dimes ($0.20).

My program successfully calculates the number of quarters, but fails to do so while working on dimes. The cause of this failure is the second for loop in the code. 0.10 >= 0.10 does not evaluate to true, so the last iteration of the loop never happens. What am I doing wrong?

Here is the code. I provided test print statements with their outputs written as comments.

#include <stdio.h>
#include <cs50.h>

int main(void) {

  // Fake user input
  float owed_coin = 4.2f;

  // Initialize coin variables
  int coin_count = 0;

  float quarters = 0.25f,
        dimes = 0.10f;

  // Calculate quarters
  while(owed_coin >= quarters) {
    owed_coin -= quarters;
    coin_count += 1;
  }
  printf("owed_coin: %.2f\ncoin_count: %d\n\n", owed_coin, coin_count);
  // Prints  owed_coin: 0.20
  //         coin_count: 16

  // Calculate dimes
  while(owed_coin >= dimes) {
    owed_coin -= dimes;
    coin_count += 1;
  }
  printf("owed_coin: %.2f\ncoin_count: %d\n\n", owed_coin, coin_count);
  // Prints  owed_coin: 0.10
  //         coin_count: 17

}

  • 6
    That's the reason not to use floating point for any kind of money calculations. – Eugene Sh. Jul 09 '18 at 18:22
  • 1
    Are you aware of https://cs50.stackexchange.com/ ? – Bob__ Jul 09 '18 at 18:23
  • The typical solution is to use integers and not floating point for internal computation, and then multiply everything by 100. For example, replace `float owed_coin = 4.2f;` with `int owed_coin = 420;` – bruceg Jul 09 '18 at 18:24
  • The assignment was meant to get you to fail like this. There are better ways to do this that don't suffer from floating point precision issues. Money amounts can also be stored in integer values. – Hans Passant Jul 09 '18 at 18:35
  • @EugeneSh.: While novices should usually avoid floating-point for currency, there are kinds of money calculations for which floating-point is suitable. – Eric Postpischil Jul 09 '18 at 18:37
  • @EricPostpischil I suppose there are, but can't think of any other than some statistical ones involving large amounts. – Eugene Sh. Jul 09 '18 at 18:39
  • @EugeneSh.: Options pricing, optimizing per Modern Portfolio Theory and other stock calculations, compounding interest over multiple periods via exponentiation rather than iteration, or simply calculating discount pricing with fractions. You have to know what you are doing, but people forget that integer arithmetic has **worse** rounding errors than floating-point (the error in `4/3` is **much** larger than in `4./3.`)—they are just more familiar with them. – Eric Postpischil Jul 09 '18 at 19:45

1 Answers1

1

Floating point comparison is generally a bad idea because floats often become non-exact and thus will not be exactly equal. As @bruceg mentioned, a good solution is to keep your monetary values in terms of cents, so that you avoid using a float.

You could replace float owed_coin = 4.2f; with int owed_coin = 420;

In terms of gathering user input into this number, here is my suggestion using scanf

int n1, n2;

printf("Please enter amount:");
scanf("%d.%2d", &n1, &n2);

owed_coin = n1*100 + n2;

Another solution allows you you keep your variables as floats, and just compare for a very small difference between the two. It can be found here: What's wrong with using == to compare floats in Java?

It uses the Java Math library, but a similar solution could look something like this in C:

while((owed_coin - dimes) >= -0.001) {
    owed_coin -= dimes;
    coin_count += 1;
}

If you want to learn more about why floating point numbers suffer small innacuracies then check this out: https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems

Jacob Boertjes
  • 963
  • 5
  • 20
  • I just tried this. It makes sense, but now the section `float owed_coin = 4.2f` represents user input. I created another variable `int int_coin = owed_coin * 100` and `int_coin` now prints 419, there is still a value loss. Is it possible to convert user input which is floating point to an integer value? –  Jul 09 '18 at 18:34
  • 2
    @HakanGuclu: Users do not enter floating-point input. They press keys on a keyboard, which are converted to characters, or they move pointers and click on things, which the software converts to other representations. If the user enters “4.20”, process that as 420 cents. If you have absolutely no way to avoid receiving the user input as a floating-point number of dollars, then, as soon as you get it, immediately convert it to an integer number of cents by multiplying it by 100 and rounding it to the nearest integer. – Eric Postpischil Jul 09 '18 at 18:35
  • @HakanGuclu Yes, due to floating point inaccuracy trying to multiply user input by 100 will cause some issues. Instead I suggest parsing user input before the decimal, multiplying that by 100, and then adding the user input which follows the decimal – Jacob Boertjes Jul 09 '18 at 18:39
  • You're right. Though I'm talking about a specific function not in the standard library but in the cs50 library that you're probably unfamiliar of, called `get_float()`, which accepts float values from the user and is a _requirement_ for the assignment. I'll figure another way of solving the problem with that function. –  Jul 09 '18 at 18:43
  • @JacobBoertjes That makes sense. Thanks! –  Jul 09 '18 at 18:46