mod gives incorrect result
Show older comments
Here is a mathematical error with a simple expression:
>> mod(-sin(pi),512)
ans =
512
Of course, pi isn't exact, so sin(pi) gives a small negative number. That isn't good, but it is perhaps excusable. However, that mod(X, 512) ever returns 512 is totally unacceptable.
2 Comments
Steve
on 31 Jul 2013
the cyclist
on 3 Aug 2013
Edited: the cyclist
on 3 Aug 2013
Another workaround would be to apply the mod function twice. [This is another indication that mod()'s output is a bit mathematically silly in this case.]
Answers (3)
Walter Roberson
on 31 Jul 2013
1 vote
In order to understand this, you need to look at the fact that -sin(pi) is a negative value that has a very small magnitude (due to standard round-off issues), so mod(-sin(pi), 512) is 512-sin(pi) . But sin(pi) is less than eps(512), so 512-sin(pi) is most closely representable as 512 itself.
This is, I agree, not what I would have expected, but I had never really thought about the matter before.
Would it be better if mod(x, P) with positive P and x in (-eps(P), 0) (exclusive) be 0? Arguable, but it is not obvious to me that that possible outcome would be better than the current outcome.
1 Comment
the cyclist
on 1 Aug 2013
In essence, what is happening here is that the tiny floating point error is going through an "amplifier". It is analogous to expecting
(1 - 2/3 - 1/3)*1e17
to be 0, but it turns out to be about 5.5.
So, there is the usual aspect of "let the buyer beware" if floating point error can give a big difference in your results.
In this particular case, though, I would argue that 512 as the "most closely representable" is less compelling, because 512 is not in the theoretical range of the mod() function, mathematically. I know it sounds odd, but I think that 0 is the most closely representable number to 512-eps in this case. Mathematically,
Limit mod(512-eps,512)
{eps->0}
is zero, not 512.
Wayne King
on 31 Jul 2013
Edited: Wayne King
on 31 Jul 2013
Are you sure you're not confusing mod() with rem()?
If you read the help for mod(), it says that
mod(x,y) returns x-floor(x/y)*y
rem(x,y) returns x-fix(x/y)*y
-sin(pi)-floor(-sin(pi)/512)*512
is 512 because
floor(-sin(pi)/512)
is -1.
so essentially 0+(1)*512 = 512
on the other hand
-sin(pi)-fix(-sin(pi)/512)*512
yields essentially
0-(0)*512=0
3 Comments
the cyclist
on 31 Jul 2013
It's admirable that MATLAB is doing what it's documentation states, but mathematically
mod(x,N)
has a range of [0,N), strictly less than N.
Steve
on 31 Jul 2013
Wayne King
on 31 Jul 2013
I think "essentially 0" is warranted here since -sin(pi) is on the order of 10^(-16). For example:
rng default;
x = randn(10,1);
max(abs(ifft(fft(x))-x))
gives a value of 8x10^(-16) but nobody would say that the example above does NOT demonstrate perfect inversion of x.
Wayne King
on 31 Jul 2013
Edited: Wayne King
on 31 Jul 2013
If anybody's interested in this, google:
"division and modulus for computer scientists"
mod() is implementing what Knuth termed "floored division". It's also described here:
Just FYI, Python implements the same operation for % as MATLAB mod(), but R appears to be different. R's %% operator appears to mimic rem() in MATLAB.
So in Python:
>>>import Numpy as np
>>>-np.sin(np.pi)%512
returns 512.
In R
> -sin(pi)%%512
returns zero like MATLAB
rem(-sin(pi),512)
Categories
Find more on Installation in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!