-
-
Notifications
You must be signed in to change notification settings - Fork 10.9k
BUG rounding error in divmod #6127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
p.s. Bug initially found by @MatthewQuenneville |
A bit further checking shows that
Of course, what is "wrong" is a bit in the eye of the beholder:
In any case, at least the A possible cause of the problem may be a check on negative remainders done in |
Given that:
It seems that Python's floor division is a bit problematic:
|
For reference, I reported a Python issue at http://bugs.python.org/issue25129 |
Not really, that could simply be a floating point rounding error, and the remainder is consistent with that. All that matters is that one can add back, and this is fine for python:
In contrast, with a numpy array:
|
Looks to me like it depends on the details of the computation
|
nvm. Yes, it looks like numpy gets the remainder wrong. |
I think the problem is that the two are not calculated together, so that the adjustment in the remainder is not propagated to the quotient. |
Indeed, CPython internally has a unique divmod function that gets called by all three operations: divmod(), floor division and remainder. |
Consider a hypothetical |
@argriffing - I know one cannot always avoid precision/rounding errors; however, the remainder and floor division should be consistent with each other, in that if |
Here's a case that's perhaps less contrieved, only using "non-small" numbers:
|
plt.plot([-.1, .2]) used to pick (in round numbers mode) [-.1, .25] as ylims due to floating point inaccuracies; change it to pick [-.1, .2] (up to floating point inaccuracies). Note that this requires working around a bug in numpy's implementation of divmod (numpy/numpy#6127). Many test images have changed! See matplotlib#5767.
plt.plot([-.1, .2]) used to pick (in round numbers mode) [-.1, .25] as ylims due to floating point inaccuracies; change it to pick [-.1, .2] (up to floating point inaccuracies). Note that this requires working around a bug in numpy's implementation of divmod (numpy/numpy#6127). Many test images have changed! See matplotlib#5767. Probably also wraps up work on matplotlib#5738, but tests are missing.
plt.plot([-.1, .2]) used to pick (in round numbers mode) [-.1, .25] as ylims due to floating point inaccuracies; change it to pick [-.1, .2] (up to floating point inaccuracies). Note that this requires working around a bug in numpy's implementation of divmod (numpy/numpy#6127). Many test images have changed! See matplotlib#5767. Probably also wraps up work on matplotlib#5738, but tests are missing.
plt.plot([-.1, .2]) used to pick (in round numbers mode) [-.1, .25] as ylims due to floating point inaccuracies; change it to pick [-.1, .2] (up to floating point inaccuracies). Note that this requires working around a bug in numpy's implementation of divmod (numpy/numpy#6127). Many test images have changed! See matplotlib#5767. Also some more progress on matplotlib#5738.
plt.plot([-.1, .2]) used to pick (in round numbers mode) [-.1, .25] as ylims due to floating point inaccuracies; change it to pick [-.1, .2] (up to floating point inaccuracies). Support for the (unused and deprecated-in-comment) "trim" keyword argument has been dropped as the way ticks are picked has changed. Many test images have changed! Implementation notes: - A bug in numpy's implementation of divmod (numpy/numpy#6127) is worked around. - The implementation of scale_range has also been cleaned. See matplotlib#5767, matplotlib#5738.
plt.plot([-.1, .2]) used to pick (in round numbers mode) [-.1, .25] as ylims due to floating point inaccuracies; change it to pick [-.1, .2] (up to floating point inaccuracies). Support for the (unused and deprecated-in-comment) "trim" keyword argument has been dropped as the way ticks are picked has changed. Many test images have changed! Implementation notes: - A bug in numpy's implementation of divmod (numpy/numpy#6127) is worked around. - The implementation of scale_range has also been cleaned. See matplotlib#5767, matplotlib#5738.
plt.plot([-.1, .2]) used to pick (in round numbers mode) [-.1, .25] as ylims due to floating point inaccuracies; change it to pick [-.1, .2] (up to floating point inaccuracies). Support for the (unused and deprecated-in-comment) "trim" keyword argument has been dropped as the way ticks are picked has changed. Many test images have changed! Implementation notes: - A bug in numpy's implementation of divmod (numpy/numpy#6127) is worked around. - The implementation of scale_range has also been cleaned. See matplotlib#5767, matplotlib#5738.
Well, I can fix the Numpy end of this, but np.float64 (scalar) inherits from Python and numpy divmod never gets called. In Python 2.7:
I think Python is doing something inconsistent here. |
There are two places for overriding divmod:
so at least everything is overridable. |
oops, |
I don't know what you exactly mean, but inserting printfs at the entry of all of these functions only results in the given functions being logged; e.g. float64/float64 doesn't seem to call array_divmod at any point. |
Oh, that is ugly. The umath module initializes the scalar type function tables when it loads. I knew it passed a number of function to a function table for the arrays, so I guess I should have expected it. I wonder if this would have a effect on the proposed uses of |
The python solution of always calling the same function for all of |
The problem currently is that numpy uses C |
Indeed, it is amazing that
prints PS: Confirmed with glibc 2.22/gcc 5.3.0 (Linux) and MSVC2015 (Windows). |
Yep,
which is incorrect because the remainder is negative but should have the same sign as the divisor (python definition). So it seems that the only safe way to have |
I don't think that's the difference (everything is positive), using |
A fix is to express the remainder in this form.
Which looks good enough. |
Agreed that the two need to go through the same path. |
I think we can get around the same path problem as long as division and |
This is apropos numpy#6127. The fix is to make the functions floor_division and remainder consistent, i.e., b * floor_division(a, b) + remainder(a, b) == a Previous to this fix remainder was computed a the C level using the '%' operator, and the result was not always consistent with the floor function. The current approach is to compute the remainder using b * (a/b - floor(a/b)) which is both consistent with the Python '%' operator and numerically consistent with floor_division implemented using the floor function. Closes numpy#6127.
Happy to see this resolved; solution looks good! |
@mhvk, I think it is actually better than Python at the moment. However, at some point it will hit the precision limits of the floats... |
There seems to be a regression now: #7224 |
plt.plot([-.1, .2]) used to pick (in round numbers mode) [-.1, .25] as ylims due to floating point inaccuracies; change it to pick [-.1, .2] (up to floating point inaccuracies). Support for the (unused and deprecated-in-comment) "trim" keyword argument has been dropped as the way ticks are picked has changed. Many test images have changed! Implementation notes: - A bug in numpy's implementation of divmod (numpy/numpy#6127) is worked around. - The implementation of scale_range has also been cleaned. See matplotlib#5767, matplotlib#5738.
plt.plot([-.1, .2]) used to pick (in round numbers mode) [-.1, .25] as ylims due to floating point inaccuracies; change it to pick [-.1, .2] (up to floating point inaccuracies). Support for the (unused and deprecated-in-comment) "trim" keyword argument has been dropped as the way ticks are picked has changed. Many test images have changed! Implementation notes: - A bug in numpy's implementation of divmod (numpy/numpy#6127) is worked around. - The implementation of scale_range has also been cleaned. See matplotlib#5767, matplotlib#5738.
plt.plot([-.1, .2]) used to pick (in round numbers mode) [-.1, .25] as ylims due to floating point inaccuracies; change it to pick [-.1, .2] (up to floating point inaccuracies). Support for the (unused and deprecated-in-comment) "trim" keyword argument has been dropped as the way ticks are picked has changed. Many test images have changed! Implementation notes: - A bug in numpy's implementation of divmod (numpy/numpy#6127) is worked around. - The implementation of scale_range has also been cleaned. See matplotlib#5767, matplotlib#5738.
This is apropos numpy#6127. The fix is to make the functions floor_division and remainder consistent, i.e., b * floor_division(a, b) + remainder(a, b) == a Previous to this fix remainder was computed a the C level using the '%' operator, and the result was not always consistent with the floor function. The current approach is to compute the remainder using b * (a/b - floor(a/b)) which is both consistent with the Python '%' operator and numerically consistent with floor_division implemented using the floor function. Closes numpy#6127.
This test only over passed due to an error arising from a bug in numpy's divmod being fixed (numpy/numpy#6127). See matplotlib#5950
This test only over passed due to an error arising from a bug in numpy's divmod being fixed (numpy/numpy#6127). See matplotlib#5950
This test only over passed due to an error arising from a bug in numpy's divmod being fixed (numpy/numpy#6127). See matplotlib#5950
The
ndarray
implementation ofdivmod
seems to do some incorrect rounding:The text was updated successfully, but these errors were encountered: