5

Given:

#include <iostream>
#include <cmath>
#include <limits>
using namespace std;

int main() {
    // your code goes here
    double h = .1;
    double x = 1;
    int nSteps = abs(x / h);

    double rem = fmod(x, h);
    cout<<"fmod output is "<<rem<<endl;
    if(abs(rem)<std::numeric_limits<double>::epsilon())
        cout<<"fmod output is almost near 0"<<endl;

    rem = remainder(x,h);
    cout<<"remainder output is "<<rem<<endl;
    if(abs(rem)<std::numeric_limits<double>::epsilon())
        cout<<"remainder output is almost near 0"<<endl;

    return 0;
}

Given int(x/h) == 10, I would have expected the fmod() result to be near 0 but what i get is .0999999999. It is a significant difference. The result of remainder() still seem acceptable. Code could be tried at http://ideone.com/9wBlva

Why this significant difference for fmod() result?

Baum mit Augen
  • 49,044
  • 25
  • 144
  • 182
umbersar
  • 1,821
  • 3
  • 24
  • 34
  • C++ and C Arent the same langauge – amanuel2 Oct 10 '16 at 20:29
  • If that does not answer your question on its own, read the docs. `fmod` and `remainder` don't do the same thing, so different results are to be expected. – Baum mit Augen Oct 10 '16 at 20:32
  • @amanuel2 u get similar results http://ideone.com/rLyS2t – umbersar Oct 10 '16 at 20:40
  • @BaummitAugen: I'm still a little confused. The documentation on [fmod](http://en.cppreference.com/w/cpp/numeric/math/fmod) states that "The floating-point remainder of the division operation x/y calculated by this function is exactly the value x - n*y, where n is x/y with its fractional part truncated.", but if I explicitly compute that myself, I get a result of 0 while fmod still returns 0.1. [Example](http://coliru.stacked-crooked.com/a/f36cbeafc648a639). If I use `float` instead of `double`, the value comes out as expected [Demo](http://coliru.stacked-crooked.com/a/5916ae514c3c94ff) – AndyG Oct 10 '16 at 20:40
  • @wanderer Just because a program happens to work in both a C++ Compiler and a C Compiler doesn't make it so , that C++ And C Are the same language. – amanuel2 Oct 10 '16 at 20:43
  • @amanuel2 The example i posted for C is C code. I should consider it end of this discussion. – umbersar Oct 10 '16 at 20:48
  • I mean, the underlying diagnosis of my dupe vote is most likely still correct because `.1` cannot be represented exactly in C++ `double`s (IEEE-754 `double`s, to be more precise), but I guess [this output](http://melpon.org/wandbox/permlink/serNuFI4wFLOpOaj) could need some more explanation. – Baum mit Augen Oct 10 '16 at 20:49
  • @BaummitAugen I see similar issues when using 2 and 0.2. I can't help but feel there's something I'm not getting, so I'm afraid of voting to reopen. – AndyG Oct 10 '16 at 20:50
  • @wanderer Two things , first of all there is no concept of namespaces in C, Second of all [**Do Not Use `using namespace std;` !!**](http://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice) . There is a reason why your question is tagged C++ , please think before tagging , so you dont spam other tags. Thankyou! – amanuel2 Oct 10 '16 at 20:51
  • @amanuel2: What do you mean there is no concept of namespaces in C++? We use them all the time. Second, for use in a small toy example like this it's no problem, though not recommended. – AndyG Oct 10 '16 at 20:52
  • @AndyG I Meant to type C . Mistype – amanuel2 Oct 10 '16 at 20:52
  • @AndyG Unsurprisingly, the problem goes away when using 1, .25 which are represented exactly ([Live](http://melpon.org/wandbox/permlink/wgw3jq541zEGuP6y)). Now the question is why `int(1/.1)` yields 10 instead of 9. – Baum mit Augen Oct 10 '16 at 20:52
  • @AndyG As of the time of your comment, I already reopened it. ;) – Baum mit Augen Oct 10 '16 at 20:53
  • The value `0.1` cannot be represented exactly in binary floating-point. The actual value of `h` is most likely `0.1000000000000000055511151231257827021181583404541015625`, which can be represented exactly. – Keith Thompson Oct 10 '16 at 21:10

1 Answers1

4

The problem you're seeing is that the version of fmod you're using appears to follow the implementation defined at cppreference:

double fmod(double x, double y)
{
    double result = std::remainder(std::fabs(x), (y = std::fabs(y)));
    if (std::signbit(result)) result += y;
    return std::copysign(result, x);
} 

std::remainder computes a very very small result, nearly zero (-5.55112e-17 when using 1 and 0.1 for me, -1.11022e-16 for 2 and 0.2). However, what's important is that the result is negative, which means std::signbit returns true, causing y to get added to the result, effectively making the result equal to y.

Note that the documentation of std::fmod doesn't say anything about using std::remainder:

The floating-point remainder of the division operation x/y calculated by this function is exactly the value x - n*y, where n is x/y with its fractional part truncated.

So if you compute the value yourself, you do end up with zero (even if you use std::round on the result instead of pure integer truncation)

We see similar problems when x is 2 and y is 0.2

double x = 2;
double y = .2;

int n = static_cast<int>(x/y);
double result = x - n*y;
std::cout << "Manual: " << result << std::endl;
std::cout << "fmod: " << std::fmod(x,y) << std::endl;

Output (gcc demo) is

Manual: 0
fmod: 0.2

However the problem is not relegated to only gcc; I also see it in MSVC and clang. In clang there is sometimes different behavior if one uses float instead of double.

This really small negative value from std::remainder comes from the fact that neither 0.1 nor 0.2 can be represented exactly in floating point math. If you change x and y to, say 2 and 0.25, then all is well.

AndyG
  • 39,700
  • 8
  • 109
  • 143
  • For `fmod`: *"is exactly the value x - n\*y, where n is x/y with its fractional part truncated."* And by `int(x/h) == 10`, `n` should be `10` and thus `fmod(x,h) == 1 - 10 * .1` which should be almost 0. So far the stuff in your answer is not enough to explain this. – Baum mit Augen Oct 10 '16 at 21:22
  • I just found a similar question asked here..http://stackoverflow.com/q/26519082/364084. – umbersar Oct 10 '16 at 21:29
  • @AndyG I am still working out the result manually as per this explanation. If it works out, I will mark it as the answer – umbersar Oct 10 '16 at 21:31
  • @wanderer Now that looks like an actual dupe. – Baum mit Augen Oct 10 '16 at 21:33
  • @BaummitAugen yes it does. I will try to manually verify to see if it does answer the question. – umbersar Oct 10 '16 at 21:36
  • @BaummitAugen: I tried to make it clear that the problem stemmed from `std::remainder` to compute `fmod` returning a negative value. Following the logic of the `fmod` implementation, it's no wonder that the result is basically `y` – AndyG Oct 10 '16 at 21:38
  • @AndyG I still don't see how that addresses my point with the truncating assignment yielding 10. – Baum mit Augen Oct 10 '16 at 21:40
  • @AndyG yes I found the explanation of std::remainder returning a -ve value along with the code for fmod implementation helpful – umbersar Oct 10 '16 at 21:42
  • @wanderer The important part here is whether or not the question itself is a dupe, less whether or not the answers there are great (which they probably aren't). If we need more/better answers, I think we should use bounties / upvotes on the original. – Baum mit Augen Oct 10 '16 at 21:42
  • 1
    @BaummitAugen: Oh I totally agree. I didn't try to justify the behavior as correct. By the documentation of `fmod`, this behavior is a bug in my opinion. It looks like the cause of that is both by using `std::remainder` and by perhaps incompletely handling a negative result. – AndyG Oct 10 '16 at 21:47
  • I am marking this as the answer and it is in my opinion a better answer than ones on http://stackoverflow.com/q/26519082/364084. It points out that the documentation of fmod() does not agree with its internal implementation due to its use of std::remainder(not mentioned in documentationn) and if we did infact tried to do what the description of fmod() said, we would have gotten 0(the expected result). And since fmod is using std::remainder internally and expecting the behaviour of remainder() to known to the implementers of fmod(), this case could have been handled in a better manner. – umbersar Oct 10 '16 at 22:29
  • As this seems to be the still current behaviour of clang and gcc, we can conclude that `fmod` is basically useless if the second parameter almost evenly divides the first, right? So you can never use it to determine this fact basically. At least for values in this range discussed here. – oarfish Jul 09 '21 at 16:13