8000 ENH: Make numpy floor_divide and remainder agree with Python `//` and `%`. by charris · Pull Request #7258 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

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

Merged
merged 6 commits into from
Feb 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions numpy/core/code_generators/ufunc_docstrings.py
Original file line number Diff line number Diff line change
Expand Up @@ -1225,8 +1225,10 @@ def add_newdoc(place, name, doc):

add_newdoc('numpy.core.umath', 'floor_divide',
"""
Return the largest integer smaller or equal to the division of the
inputs.
Return the largest integer smaller or equal to the division of the inputs.
It is equivalent to the Python ``//`` operator and pairs with the
Python ``%`` (`remainder`), function so that ``b = a % b + b * (a // b)``
up to roundoff.

Parameters
----------
Expand All @@ -1243,6 +1245,7 @@ def add_newdoc(place, name, doc):

See Also
--------
remainder : Remainder complementary to floor_divide.
divide : Standard division.
floor : Round a number to the nearest integer toward minus infinity.
ceil : Round a number to the nearest integer toward infinity.
Expand Down Expand Up @@ -2689,9 +2692,9 @@ def add_newdoc(place, name, doc):
"""
Return element-wise remainder of division.

Computes ``x1 - floor(x1 / x2) * x2``, the result has the same sign as
the divisor `x2`. It is equivalent to the Python modulus operator
``x1 % x2`` and should not be confused with the Matlab(TM) ``rem``
Computes the remainder complementary to the `floor_divide` function. It is
equivalent to the Python modulus operator``x1 % x2`` and has the same sign
as the divisor `x2`. It should not be confused with the Matlab(TM) ``rem``
function.

Parameters
Expand All @@ -2707,11 +2710,12 @@ def add_newdoc(place, name, doc):
Returns
-------
y : ndarray
The remainder of the quotient ``x1/x2``, element-wise. Returns a
scalar if both `x1` and `x2` are scalars.
The element-wise remainder of the quotient ``floor_divide(x1, x2)``.
Returns a scalar if both `x1` and `x2` are scalars.

See Also
--------
floor_divide : Equivalent of Python ``//`` operator.
fmod : Equivalent of the Matlab(TM) ``rem`` function.
divide, floor

Expand Down
1 change: 1 addition & 0 deletions numpy/core/include/numpy/halffloat.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ int npy_half_signbit(npy_half h);
npy_half npy_half_copysign(npy_half x, npy_half y);
npy_half npy_half_spacing(npy_half h);
npy_half npy_half_nextafter(npy_half x, npy_half y);
npy_half npy_half_divmod(npy_half x, npy_half y, npy_half *modulus);

/*
* Half-precision constants
Expand Down
4 changes: 4 additions & 0 deletions numpy/core/include/numpy/npy_math.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,16 +309,20 @@ double npy_deg2rad(double x);
double npy_rad2deg(double x);
double npy_logaddexp(double x, double y);
double npy_logaddexp2(double x, double y);
double npy_divmod(double x, double y, double *modulus);

float npy_deg2radf(float x);
float npy_rad2degf(float x);
float npy_logaddexpf(float x, float y);
float npy_logaddexp2f(float x, float y);
float npy_divmodf(float x, float y, float *modulus);

npy_longdouble npy_deg2radl(npy_longdouble x);
npy_longdouble npy_rad2degl(npy_longdouble x);
npy_longdouble npy_logaddexpl(npy_longdouble x, npy_longdouble y);
npy_longdouble npy_logaddexp2l(npy_longdouble x, npy_longdouble y);
npy_longdouble npy_divmodl(npy_longdouble x, npy_longdouble y,
npy_longdouble *modulus);

#define npy_degrees npy_rad2deg
#define npy_degreesf npy_rad2degf
Expand Down
2 changes: 2 additions & 0 deletions numpy/core/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,8 @@ def generate_umath_c(ext, build_dir):

umath_deps = [
generate_umath_py,
join('include', 'numpy', 'npy_math.h'),
join('include', 'numpy', 'halffloat.h'),
join('src', 'multiarray', 'common.h'),
join('src', 'private', 'templ_common.h.src'),
join('src', 'umath', 'simd.inc.src'),
Expand Down
11 changes: 11 additions & 0 deletions numpy/core/src/npymath/halffloat.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,17 @@ int npy_half_ge(npy_half h1, npy_half h2)
return npy_half_le(h2, h1);
}

npy_half npy_half_divmod(npy_half h1, npy_half h2, npy_half *modulus)
{
float fh1 = npy_half_to_float(h1);
float fh2 = npy_half_to_float(h2);
float div, mod;

div = npy_divmodf(fh1, fh2, &mod);
*modulus = npy_float_to_half(mod);
return npy_float_to_half(div);
}



/*
Expand Down
48 changes: 48 additions & 0 deletions numpy/core/src/npymath/npy_math.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,54 @@ double npy_log2(double x)
}
}

/*
* Python version of divmod.
*
* The implementation is mostly copied from cpython 3.5.
*/
@type@
npy_divmod@c@(@type@ a, @type@ b, @type@ *modulus)
{
@type@ div, mod, floordiv;

mod = npy_fmod@c@(a, b);

if (!b) {
/* If b == 0, return result of fmod. For IEEE is nan */
*modulus = mod;
return mod;
}

/* a - mod should be very nearly an integer multiple of b */
div = (a - mod) / b;

/* adjust fmod result to conform to Python convention of remainder */
if (mod) {
if ((b < 0) != (mod < 0)) {
mod += b;
div -= 1.0@c@;
}
}
else {
/* if mod is zero ensure correct sign */
mod = (b > 0) ? 0.0@c@ : -0.0@c@;
}

/* snap quotient to nearest integral value */
if (div) {
floordiv = npy_floor(div);
if (div - floordiv > 0.5@c@)
floordiv += 1.0@c@;
}
else {
/* if div is zero ensure correct sign */
floordiv = (a / b > 0) ? 0.0@c@ : -0.0@c@;
}

*modulus = mod;
return floordiv;
}

#undef LOGE2
#undef LOG2E
#undef RAD2DEG
Expand Down
25 changes: 10 additions & 15 deletions numpy/core/src/umath/loops.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -1696,7 +1696,8 @@ NPY_NO_EXPORT void
BINARY_LOOP {
const @type@ in1 = *(@type@ *)ip1;
const @type@ in2 = *(@type@ *)ip2;
*((@type@ *)op1) = npy_floor@c@(in1/in2);
@type@ mod;
*((@type@ *)op1) = npy_divmod@c@(in1, in2, &mod);
}
}

Expand All @@ -1706,8 +1707,7 @@ NPY_NO_EXPORT void
BINARY_LOOP {
const @type@ in1 = *(@type@ *)ip1;
const @type@ in2 = *(@type@ *)ip2;
const @type@ div = in1/in2;
*((@type@ *)op1) = in2*(div - npy_floor@c@(div));
npy_divmod@c@(in1, in2, (@type@ *)op1);
}
}

Expand Down Expand Up @@ -2011,25 +2011,20 @@ NPY_NO_EXPORT void
HALF_floor_divide(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
{
BINARY_LOOP {
const float in1 = npy_half_to_float(*(npy_half *)ip1);
const float in2 = npy_half_to_float(*(npy_half *)ip2);
*((npy_half *)op1) = npy_float_to_half(npy_floorf(in1/in2));
const npy_half in1 = *(npy_half *)ip1;
const npy_half in2 = *(npy_half *)ip2;
npy_half mod;
*((npy_half *)op1) = npy_half_divmod(in1, in2, &mod);
}
}

NPY_NO_EXPORT void
HALF_remainder(char **args, npy_intp *dimensions, npy_intp *steps, void *NPY_UNUSED(func))
{
BINARY_LOOP {
const float in1 = npy_half_to_float(*(npy_half *)ip1);
const float in2 = npy_half_to_float(*(npy_half *)ip2);
const float res = npy_fmodf(in1,in2);
if (res && ((in2 < 0) != (res < 0))) {
*((npy_half *)op1) = npy_float_to_half(res + in2);
}
else {
*((npy_half *)op1) = npy_float_to_half(res);
}
const npy_half in1 = *(npy_half *)ip1;
const npy_half in2 = *(npy_half *)ip2;
npy_half_divmod(in1, in2, (npy_half *)op1);
}
}

Expand Down
101 changes: 53 additions & 48 deletions numpy/core/src/umath/scalarmath.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ static void
}

#define @name@_ctype_floor_divide @name@_ctype_divide

static void
@name@_ctype_remainder(@type@ a, @type@ b, @type@ *out) {
if (a == 0 || b == 0) {
Expand Down Expand Up @@ -268,20 +269,40 @@ static void
/**begin repeat
* #name = float, double, longdouble#
* #type = npy_float, npy_double, npy_longdouble#
* #c = f, , l#
*/
static @type@ (*_basic_@name@_floor)(@type@);
static @type@ (*_basic_@name@_sqrt)(@type@);
static @type@ (*_basic_@name@_fmod)(@type@, @type@);

#define @name@_ctype_add(a, b, outp) *(outp) = (a) + (b)
#define @name@_ctype_subtract(a, b, outp) *(outp) = (a) - (b)
#define @name@_ctype_multiply(a, b, outp) *(outp) = (a) * (b)
#define @name@_ctype_divide(a, b, outp) *(outp) = (a) / (b)
#define @name@_ctype_true_divide @name@_ctype_divide
#define @name@_ctype_floor_divide(a, b, outp) \
*(outp) = _basic_@name@_floor((a) / (b))


static void
@name@_ctype_floor_divide(@type@ a, @type@ b, @type@ *out) {
@type@ mod;

*out = npy_divmod@c@(a, b, &mod);
}


static void
@name@_ctype_remainder(@type@ a, @type@ b, @type@ *out) {
npy_divmod@c@(a, b, out);
}


static void
@name@_ctype_divmod(@type@ a, @type@ b, @type@ *out1, @type@ *out2) {
*out1 = npy_divmod@c@(a, b, out2);
}


/**end repeat**/

static npy_half (*_basic_half_floor)(npy_half);
static npy_half (*_basic_half_sqrt)(npy_half);
static npy_half (*_basic_half_fmod)(npy_half, npy_half);

Expand All @@ -294,9 +315,26 @@ static npy_half (*_basic_half_fmod)(npy_half, npy_half);
#define half_ctype_divide(a, b, outp) *(outp) = \
npy_float_to_half(npy_half_to_float(a) / npy_half_to_float(b))
#define half_ctype_true_divide half_ctype_divide
#define half_ctype_floor_divide(a, b, outp) \
*(outp) = npy_float_to_half(_basic_float_floor(npy_half_to_float(a) / \
npy_half_to_float(b)))


static void
half_ctype_floor_divide(npy_half a, npy_half b, npy_half *out) {
npy_half mod;

*out = npy_half_divmod(a, b, &mod);
}


static void
half_ctype_remainder(npy_half a, npy_half b, npy_half *out) {
npy_half_divmod(a, b, out);
}


static void
half_ctype_divmod(npy_half a, npy_half b, npy_half *out1, npy_half *out2) {
*out1 = npy_half_divmod(a, b, out2);
}

/**begin repeat
* #name = cfloat, cdouble, clongdouble#
Expand Down Expand Up @@ -344,45 +382,31 @@ static npy_half (*_basic_half_fmod)(npy_half, npy_half);
(outp)->imag = (in1i*rat - in1r)*scl; \
} \
} while(0)

#define @name@_ctype_true_divide @name@_ctype_divide

#define @name@_ctype_floor_divide(a, b, outp) do { \
(outp)->real = _basic_@rname@_floor \
(((a).real*(b).real + (a).imag*(b).imag) / \
((b).real*(b).real + (b).imag*(b).imag)); \
@rname@_ctype_floor_divide( \
((a).real*(b).real + (a).imag*(b).imag), \
((b).real*(b).real + (b).imag*(b).imag), \
&((outp)->real)); \
(outp)->imag = 0; \
} while(0)
/**end repeat**/

/**begin repeat
* #name = float, double, longdouble#
* #type = npy_float, npy_double, npy_longdouble#
*/
static void
@name@_ctype_remainder(@type@ a, @type@ b, @type@ *out) {
@type@ tmp = a/b;
*out = b * (tmp - _basic_@name@_floor(tmp));
}
/**end repeat**/

static void
half_ctype_remainder(npy_half a, npy_half b, npy_half *out) {
float tmp, fa = npy_half_to_float(a), fb = npy_half_to_float(b);
float_ctype_remainder(fa, fb, &tmp);
*out = npy_float_to_half(tmp);
}


/**begin repeat
* #name = byte, ubyte, short, ushort, int, uint, long, ulong,
* longlong, ulonglong, half, float, double, longdouble,
* cfloat, cdouble, clongdouble#
* longlong, ulonglong, cfloat, cdouble, clongdouble#
*/
#define @name@_ctype_divmod(a, b, out, out2) { \
@name@_ctype_floor_divide(a, b, out); \
@name@_ctype_remainder(a, b, out2); \
}
9F9E /**end repeat**/


/**begin repeat
* #name = float, double, longdouble#
* #type = npy_float, npy_double, npy_longdouble#
Expand Down Expand Up @@ -1665,25 +1689,6 @@ get_functions(PyObject * mm)
_basic_clongdouble_pow = funcdata[j + 5];
Py_DECREF(obj);

/* Get the floor functions */
obj = PyObject_GetAttrString(mm, "floor");
if (obj == NULL) {
goto fail;
}
funcdata = ((PyUFuncObject *)obj)->data;
signatures = ((PyUFuncObject *)obj)->types;
i = 0;
j = 0;
while(signatures[i] != NPY_FLOAT) {
i += 2;
j++;
}
_basic_half_floor = funcdata[j - 1];
_basic_float_floor = funcdata[j];
_basic_double_floor = funcdata[j + 1];
_basic_longdouble_floor = funcdata[j + 2];
Py_DECREF(obj);

/* Get the sqrt functions */
obj = PyObject_GetAttrString(mm, "sqrt");
if (obj == NULL) {
Expand Down
Loading
0