2

I am a beginner and this is (a simplified version of) part of a code I am writing:

1.prompt user to input a number (float dollar amount), lets say user will only key in amount which are multiples of 0.05 ;

2.calculate the (int)number of nickels in that dollar amount.

int main(void)
{
    float amount = 0;
    printf("$ ");
    amount = get_float();

    int n = 0;
    while ((amount - 0.05) >= 0)
    {
        amount -= 0.05;
        n ++;
    } 
    printf("%i nickels\n", n);

}

It works for any amount <=0.3 or >=1.2 (so $2 has 40 nickels, and $0.30 has 6 nickels);

but one nickel less for any amount between/including 0.35 and 1.15 (so $0.35 has 6 nickels, and $1 has 19 nickels)

I did step by step printing, and it turns out that for any amount between 0.35 and 1.15, the program stops at the last 0.050000, instead of going back to the loop and loop it one last time.

What went wrong? Please help... Thanks so much for your time!

cashmisa
  • 38
  • 3
  • 4
    related: is floating point math broken? – Sourav Ghosh Apr 05 '17 at 10:08
  • Never use floating point to do currency calculations. Floating point is not designed to work for currency and will cause results that are wrong, inaccurate and sometimes even illegal. Use integers to count cents and check what your laws say about rounding. Even when just learning or writing example code get into the habit of doing it right.Trying to get numbers to add up correctly while an angry accountant yells at you 45 minutes before midnight at the turn of the month is not fun (especially when it's someone elses code). – Art Apr 05 '17 at 11:09

3 Answers3

4

You are hitting up against floating point rounding.

Floating points are never exactly accurate, try calculating

printf("one third 'ish' = %6.20lf\n", (double) 1.0/ (double) 3.0);

Look at the answer, it's not quite accurate. :)

Your best bet is to accept the input, convert it to an integer (ie, cents) by multiplying by 100, removing any decimals using round() and assigning to a long.

    #include <math.h> // to get the round() function
.
.
.
    long amountInCents = round(amount * 100);

Then, change your code to compare against 5 cents, not $0.05, and to reduce by 5 cents, not $0.05

This way, you get rid of all 'rounding' issues.

EDIT: cmaster showed me that using floor() didn't work, using $0.57 as input, when using 'double' variables (not float), $0.57 converted to (long) 56! He supplied code and I couldn't believe my eyes!

Lyall Pearce
  • 181
  • 1
  • 5
  • I see, thanks Lyall Pearce, this is very helpful! I will bear in mind the floating point rounding for future :) – cashmisa Apr 06 '17 at 01:38
  • and if I were to edit I would have used int() instead of round(), but looking at your suggestion I looked up the differences and learned a little more. Thanks again. – cashmisa Apr 06 '17 at 02:32
0

The is no bug in your code in the strictest sense as the underlying reasoning is correct; however note that the value 0.35 cannot be represented exactly in the float datatype, the same holds for 0.05; as the division (what basically is the sematics of the code in the question) is implemented via repeated subtraction, the error caused by the inability to exactly represent the desired values accumulates. The result is that the number of subtractions is different from the analytically expected result, as the termination condition is reached too soon. The float datatype is not suitable for financial calculations, even in such a very small everyday example.

This is terrible, but it is a matter of fact.

Codor
  • 17,447
  • 9
  • 29
  • 56
  • Thanks Codor for the explanation, I understand now and will not use float in this type of situations again :) – cashmisa Apr 06 '17 at 01:43
-1

I found a fix. This is working

enter code here

while ((amount - 0.0499) >= 0)
{
    amount -= 0.05;
    n ++;
} 

while scanning .35 amount as float the actual amount value is 0.3499

shiyaz t
  • 27
  • 7