0

I am currently writing a greedy algorithm, but I have stumbled upon a problem when comparing floats.

I would use code like this:

float f = 0;   

if (f == 0) {
   // code
}

I have tested this on a seperate program and it worked fine, but not on the program I am working on.

Here is an extract from my own program.

float chf2 = fmod(chf, 0.1);
float ch3 = chf - chf2;

if (chf2 == 0) {

    /* Divide user's number by 0.1 */

    float ch3 = chf / 0.1;

    /* Round the number */

    int ch4 = round(ch3);

    /* Print the amount of coins and end */

    printf("%d\n", ch4 + coin2);
    return 0;
}

Oddly, this seems to work with a previous if statement that checks when a fmod of 0.25 from the user's input.

Is there a better way of checking if a float is equal to another float?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Argarak
  • 93
  • 1
  • 2
  • 6
  • 2
    You cannot compare floating point numbers for equality because they can easily be rounded by some arithmetic operations, because they cannot have infinite precision. You can set a positive reasonably small tolerance and then something like `if (fabs(x - y) < tolerance))`. – Iharob Al Asimi Mar 16 '15 at 18:51
  • 1
    Your program's comment `Print the amount of coins` tips me off that this is about money, and `float` or `double` is *not* the way to go about it. Use integer values for cents/pennies, as numerous previous **SO** answers have advised. – Weather Vane Mar 16 '15 at 18:56
  • To see the significant digits of some `float` like `ch3`, use `#include printf("%.*e\n", FLT_DECIMAL_DIG -1, ch3);`. This will aid in your debugging. – chux - Reinstate Monica Mar 16 '15 at 19:25
  • 1
    @Weather Vane Do not agree with advice about coding money. The solution is to understand and deal with the issues should code uses `double`, `int` or `decima128`. Successful and poor financially code can be written with binary FP, decimal FP or integers. It is not the underlying type that is the problem. OTOH, do agree not to use `float` as its precision/range is far too often too small. – chux - Reinstate Monica Mar 16 '15 at 19:32
  • @chux *"to be or not to be"* perhaps the question has the wrong title. I stand by my comment: problems involving coins and change are invariably best solved with `int` type. For example, if answering *"how do I cut a pea properly with an axe?"* you could learn a lot about an axe but the correct answer is not to use an axe. – Weather Vane Mar 16 '15 at 19:35
  • 1
    @Weather Vane Agree that if the extent of "money" is counting/adding/subtracting coins/bills, `int` is fine. I deal with interest rates, cost per product in fractions of a cents and internationalizations issues - `int` is insufficient. – chux - Reinstate Monica Mar 16 '15 at 19:40
  • @WeatherVane I have to use floats if I want to use numbers like 0.65 unless I multiply the number by 100. That could work I suppose. – Argarak Mar 16 '15 at 19:49
  • @Argarak 0.65 squalids in `float` is 65 sequins in `int`. I suggest you research previously similar questions. – Weather Vane Mar 16 '15 at 19:51
  • 2
    @iharob: Yes, you certainly *can* compare floating-point numbers for equality. It's just tricky. It can be done reliably *if* the values are restricted to those that are exactly representable. If you're dealing with money in dollars and cents, though, you're very likely to get values that *can't* be represented exactly -- and neither the compiler nor the run-time system is likely to warn you if you stray from the set of "safe" floating-point numbers into "unsafe" floating-point numbers. – Keith Thompson Mar 16 '15 at 19:53
  • @chux in your world I guess you would probably be using `>=` and `<=` and not trying for an equality. – Weather Vane Mar 16 '15 at 20:02
  • This question isn't a duplicate of the one people are marking it as a dupe of. "Why is floating-point math broken" is a closer dupe, but what's going on here is actually a tiny bit subtle. `fmod` always returns an exact result, so you really do get the remainder after division by the `double` you get when you write `0.1`. The trick is that you don't get 1/10 when you write `0.1`. – tmyklebu Mar 16 '15 at 20:22

1 Answers1

2

Your code works correctly. Your expectations might need a little bit of adjusting, however.

The fmod function always returns an exact result---when you write c = fmod(a, b), c is a number such that c + k*b (evaluated in infinite precision) exactly equals a for some integer k. So your code is actually sound---if (c == 0) will trigger exactly when a is exactly a multiple of b.

You took b to be the double 0.1, which is actually the fraction 3602879701896397/36028797018963968, not the fraction 1/10. So if I compute fmod(1, 0.1), I should expect to get the fraction (36028797018963968 % 3602879701896397) / 36028797018963968, which is 3602879701896395 36028797018963968. That's very slightly smaller than the double called 0.1.

This also explains why your code meets your expectations when you use 0.25 instead of 0.1; 0.25 gives you 1/4 exactly.

You might read this floating-point guide to learn about the representation of floating-point numbers.

tmyklebu
  • 13,915
  • 3
  • 28
  • 57
  • Thanks for your anwser! After looking at the guide, I got an idea to find out how to round something to two decimal places. I found this post: http://stackoverflow.com/questions/1343890/rounding-number-to-2-decimal-places-in-c . I managed to fix it by adding a new line: 'float roundedValue = ceilf(valueToRound * 100) / 100;' This made the code work perfectly. – Argarak Mar 16 '15 at 21:46