0

My goal is to check if there is any remainder left when dividing 2 floats, and if there is, give that remainder back to the user.

Given the following code, I had expected that fmod(2, 0.2) would be 0, however, I get back 0.2. I read that this has to do with floating point problems. But is there any way this can be done properly?

int main() {
    float a = 2.0;
    float b = 0.2;

    float rem = fmod(a, b);

    if (rem > 0) {
        std::cout << "There is a remainder: " << rem << std::endl;
    } else {
        std::cout << "No remainder: " << rem << std::endl;
    }
}

Output: There is a remainder: 0.2

Moody
  • 851
  • 2
  • 9
  • 23
  • 3
    To the people who improperly voted to close this as a duplicate of [that question](https://stackoverflow.com/questions/588004/is-floating-point-math-broken): The fact that floating-point arithmetic uses rounding does not mean you just give up and accept it. This question asks for a solution to a specific problem, and solutions exist and are not discussed in that other question. – Eric Postpischil Apr 03 '19 at 03:04
  • We need to clarify the question. As you may be aware, `.2` in source code is converted to a `double` value that is generally not exactly .2 (and `.2f` is converted to a `float` that is not exactly .2). So, first, is the problem to be solved that you want to know if a `float` `a` is or is not exactly a multiple of a `float` `b`, or you want to know if a `float` `a` is or is not exactly a multiple of some mathematical number *b*? If the latter, what form do you have *b* in—is it always a decimal numeral? Tell us more about the context in which this occurs. – Eric Postpischil Apr 03 '19 at 11:37

1 Answers1

1

Yes your hunch is correct. std::fmod is computing

std::fmod(2.0f, 0.20000000298023223876953125f)

where the second parameter is the closest IEEE754 (assume your plaform uses that) float to 0.2.

Luckily though mathematical modulus is distributive across multiplication, so you could repose as

double rem = (long long)std::round(a * 10) % (long long)std::round(b * 10) / 10.0;

using a larger power of 10 according to the number of decimal places required to represent the original problem.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • Thanks mate, just to be sure: shouldn't it be this: `rem = std::min(a, std::abs(rem - 0.2));` (Note `a` instead of `rem`) – Moody Apr 02 '19 at 21:18
  • No, my intention was to correct the floating point in another statement. – Bathsheba Apr 02 '19 at 21:21
  • I truly appreciate the help mate. In this particular case, after I have casted the result `rem` to float, how would I check if there actually is a remainder? Because the test `rem > 0` currently passes. Should I set it to `rem > 0.001`? – Moody Apr 02 '19 at 21:31
  • @Moody: Don't forget to round the `double` to the 14th significant figure before taking the cast. – Bathsheba Apr 02 '19 at 21:32
  • Actually I think I prefer the original idea. – Bathsheba Apr 02 '19 at 21:41
  • Thanks truly, for the help - unfortunately, it is a bit unclear from your latest edit how I can obtain the remainder (if there is any) to pass the print statement in my question. Is the condition I have in my if statement correct? How can I apply this to your edit? – Moody Apr 02 '19 at 21:45
  • Sorry. I'm being a turkey. You need to divide by `10.0` so the expression is the remainder. – Bathsheba Apr 02 '19 at 21:46
  • 1
    Haha no you're not - and thanks - it appears to work now! I appreciate it man – Moody Apr 02 '19 at 21:50
  • 2
    *which actually takes double not float arguments* - `fmod()` has overloads for float and long double, btw. C++ isn't like C where you should be using `fmodf()` when working with floats (or the `` version). – Shawn Apr 02 '19 at 21:55
  • @Shawn: Yes the fact that I'm being a turkey is already established - edited. Twice. – Bathsheba Apr 02 '19 at 21:56
  • To get similar result to `fmod()`, code here should use `trunc(a*10)`, not `round(a*10)`. – chux - Reinstate Monica Apr 02 '19 at 22:17
  • This is subject to multuple rounding errors. Solutions with no more error than is necessary in the final result exist. For example, if |a|>½, then I think `remainder(remainder(a, 1) + remainder(4*a, 1), 1)/5` may have no error until the final operation, which means its result will be as close as possible to the ideal result (for the symmetric residue, still needs adjustment for `fmod`). Oddly, rounding errors may occur for smaller values. I need to think more about that. – Eric Postpischil Apr 03 '19 at 03:11
  • @EricPostpischil: I look forward to seeing that answer. (I'll use my reopen hammer if it's closed as a duplicate again prior to your answering.) – Bathsheba Apr 03 '19 at 07:28