-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
ENH: Make numpy floor_divide and remainder agree with Python //
and %
.
#7258
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
Conversation
3fb9621
to
c3ee1f9
Compare
Note that pymodf uses fmod, as does Python. That function alone takes 4x longer to run than the old code :( That seems to be the price for a little bit more accuracy). |
fb2bd32
to
dc9d6ed
Compare
if (div) { | ||
floordiv = npy_floor(div); | ||
if (div - floordiv > 0.5) | ||
floordiv += 1.0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0.5@c@, 1.0@c@ ? Probably doesn't matter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
The new function is taken from the Python version of float_divmod and computes the result of floor_division and modulus together so that they can be kept compatible. This should also result in the '//' and '%' operators applied to np.float64 and Python float returning the same values. The intent is to implement the ufuncs floor_divide and remainder using the npy_divmod so that their results will also match those of '//' and '%' while providing consistency between the two. Note that npy_divmod uses fmod, which is very slow. As a result, the floor_division and remainder functions are about 4x slower than the previous versions based on the floor function, but should be more accurate.
The following numpy scalar floating functions are reimplemented using the npy_divmod function. - remainder ('%') - floor_division ('//') - divmod Note that numpy scalars warn on zero division rather than raise. divmod example, Python floats In [1]: a = 78 * 6e-8 In [2]: b = 6e-8 In [3]: divmod(a, b) Out[3]: (77.0, 5.999999999999965e-08) Before this commit numpy float64 gave In [4]: divmod(float64(a), float64(b)) Out[4]: (78.0, 0.0) After this commit numpy float64 gives In [4]: divmod(float64(a), float64(b)) Out[4]: (77.0, 5.9999999999999651e-08)
The floor function is no longer needed in scalarmath as its use has been replaced by the new pymodf function.
The following numpy ufuncs are reimplemented using the npy_divmod function. - remainder ('%') - floor_divide ('//') Example of differences Currently In [1]: a = np.array(78 * 6e-8) In [2]: b = np.array(6e-8) In [3]: a // b Out[3]: 77.0 In [4]: a % b Out[4]: 5.9999999999999651e-08 Previously In [1]: a = np.array(78 * 6e-8) In [2]: b = np.array(6e-8) In [3]: a // b Out[3]: 78.0 In [4]: a % b Out[4]: 0.0 The first agrees with the Python values for the same operation and is a bit more accurate for the input values as represented internally. Closes numpy#7224.
dc9d6ed
to
bb05b60
Compare
I think it should be clarified what the exact (in floating point) relation between floor_divide and remainder is, if it is known, and reflect that in the tests in the roundoff cases, in case such invariant is known. In cases where it's known that rounding error can exist, |
Add tests for some corner cases involving inf, zero, and nan. Check that small integers are handled exactly.
The floor_divide (//) and remainder (%) functions are complementary in the sense that a ~= (a % b) + (a // b).
bb05b60
to
542c88b
Compare
Updated. |
@pv To give an example where equality fails, suppose |
ENH: Make numpy floor_divide and remainder agree with Python `//` and `%`.
This fixes the problems with divmod in #7224 and brings divmod for numpy float64 scalars and arrays into agreement with divmod for Python floats. Tests are added to enforce the new behavior and assures that the results for small integers scaled by powers of two are exact.
Too keep the scalar and array math in sync, both the scalar math and loop code is based on a new
npy_divmod
function innpy_math
.EDIT: I changed the name to
npy_divmod
.divmod example, Python floats
Before this commit numpy float64 gave
After this commit numpy float64 gives