Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b55eacd

Browse files
committed
BUG: core: implement a long-int loop for ldexp, for cases where int != long (#1633)
long != int on many 64-bit platforms, so a second ufunc loop is needed to handle ldexp long int inputs. (cherry picked from commit 93f7521)
1 parent 0e792e6 commit b55eacd

File tree

5 files changed

+80
-12
lines changed

5 files changed

+80
-12
lines changed

numpy/core/src/umath/loops.c.src

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,36 @@ NPY_NO_EXPORT void
11231123
*((@type@ *)op1) = ldexp@c@(in1, in2);
11241124
}
11251125
}
1126+
1127+
NPY_NO_EXPORT void
1128+
@TYPE@_ldexp_long(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
1129+
{
1130+
/*
1131+
* Additional loop to handle long integer inputs (cf. #866, #1633).
1132+
* long != int on many 64-bit platforms, so we need this second loop
1133+
* to handle the default integer type.
1134+
*/
1135+
BINARY_LOOP {
1136+
const @type@ in1 = *(@type@ *)ip1;
1137+
const long in2 = *(long *)ip2;
1138+
if (((int)in2) == in2) {
1139+
/* Range OK */
1140+
*((@type@ *)op1) = ldexp@c@(in1, ((int)in2));
1141+
}
1142+
else {
1143+
/*
1144+
* Outside int range -- also ldexp will overflow in this case,
1145+
* given that exponent has less bits than int.
1146+
*/
1147+
if (in2 > 0) {
1148+
*((@type@ *)op1) = ldexp@c@(in1, NPY_MAX_INT);
1149+
}
1150+
else {
1151+
*((@type@ *)op1) = ldexp@c@(in1, NPY_MIN_INT);
1152+
}
1153+
}
1154+
}
1155+
}
11261156
#endif
11271157

11281158
#define @TYPE@_true_divide @TYPE@_divide

numpy/core/src/umath/loops.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1675,6 +1675,8 @@ FLOAT_frexp(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
16751675
#ifdef HAVE_LDEXPF
16761676
NPY_NO_EXPORT void
16771677
FLOAT_ldexp(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
1678+
NPY_NO_EXPORT void
1679+
FLOAT_ldexp_long(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
16781680
#endif
16791681

16801682
#define FLOAT_true_divide FLOAT_divide
@@ -1827,6 +1829,8 @@ DOUBLE_frexp(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
18271829
#ifdef HAVE_LDEXP
18281830
NPY_NO_EXPORT void
18291831
DOUBLE_ldexp(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
1832+
NPY_NO_EXPORT void
1833+
DOUBLE_ldexp_long(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
18301834
#endif
18311835

18321836
#define DOUBLE_true_divide DOUBLE_divide
@@ -1979,6 +1983,8 @@ LONGDOUBLE_frexp(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(fu
19791983
#ifdef HAVE_LDEXPL
19801984
NPY_NO_EXPORT void
19811985
LONGDOUBLE_ldexp(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
1986+
NPY_NO_EXPORT void
1987+
LONGDOUBLE_ldexp_long(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
19821988
#endif
19831989

19841990
#define LONGDOUBLE_true_divide LONGDOUBLE_divide

numpy/core/src/umath/loops.h.src

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,8 @@ NPY_NO_EXPORT void
268268
#ifdef HAVE_LDEXP@C@
269269
NPY_NO_EXPORT void
270270
@TYPE@_ldexp(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
271+
NPY_NO_EXPORT void
272+
@TYPE@_ldexp_long(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func));
271273
#endif
272274

273275
#define @TYPE@_true_divide @TYPE@_divide

numpy/core/src/umath/umathmodule.c.src

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ static PyUFuncGenericFunction frexp_functions[] = {
164164
};
165165

166166
static void * blank3_data[] = { (void *)NULL, (void *)NULL, (void *)NULL};
167+
static void * blank6_data[] = { (void *)NULL, (void *)NULL, (void *)NULL,
168+
(void *)NULL, (void *)NULL, (void *)NULL};
167169
static char frexp_signatures[] = {
168170
#ifdef HAVE_FREXPF
169171
PyArray_FLOAT, PyArray_FLOAT, PyArray_INT,
@@ -174,22 +176,35 @@ static char frexp_signatures[] = {
174176
#endif
175177
};
176178

179+
#if NPY_SIZEOF_LONG == NPY_SIZEOF_INT
180+
#define LDEXP_LONG(typ) typ##_ldexp
181+
#else
182+
#define LDEXP_LONG(typ) typ##_ldexp_long
183+
#endif
184+
177185
static PyUFuncGenericFunction ldexp_functions[] = {
178186
#ifdef HAVE_LDEXPF
179187
FLOAT_ldexp,
188+
LDEXP_LONG(FLOAT),
180189
#endif
181-
DOUBLE_ldexp
190+
DOUBLE_ldexp,
191+
LDEXP_LONG(DOUBLE)
182192
#ifdef HAVE_LDEXPL
183-
,LONGDOUBLE_ldexp
193+
,
194+
LONGDOUBLE_ldexp,
195+
LDEXP_LONG(LONGDOUBLE)
184196
#endif
185197
};
186198

187199
static char ldexp_signatures[] = {
188200
#ifdef HAVE_LDEXPF
189201
PyArray_FLOAT, PyArray_INT, PyArray_FLOAT,
202+
PyArray_FLOAT, PyArray_LONG, PyArray_FLOAT,
190203
#endif
204+
PyArray_DOUBLE, PyArray_INT, PyArray_DOUBLE,
191205
PyArray_DOUBLE, PyArray_LONG, PyArray_DOUBLE
192206
#ifdef HAVE_LDEXPL
207+
,PyArray_LONGDOUBLE, PyArray_INT, PyArray_LONGDOUBLE
193208
,PyArray_LONGDOUBLE, PyArray_LONG, PyArray_LONGDOUBLE
194209
#endif
195210
};
@@ -213,14 +228,14 @@ InitOtherOperators(PyObject *dictionary) {
213228
PyDict_SetItemString(dictionary, "frexp", f);
214229
Py_DECREF(f);
215230

216-
num = 1;
231+
num = 2;
217232
#ifdef HAVE_LDEXPL
218-
num += 1;
233+
num += 2;
219234
#endif
220235
#ifdef HAVE_LDEXPF
221-
num += 1;
236+
num += 2;
222237
#endif
223-
f = PyUFunc_FromFuncAndData(ldexp_functions, blank3_data, ldexp_signatures, num,
238+
f = PyUFunc_FromFuncAndData(ldexp_functions, blank6_data, ldexp_signatures, num,
224239
2, 1, PyUFunc_None, "ldexp",
225240
"Compute y = x1 * 2**x2.",0);
226241
PyDict_SetItemString(dictionary, "ldexp", f);

numpy/core/tests/test_umath.py

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -387,14 +387,29 @@ def test_nan_any(self):
387387

388388

389389
class TestLdexp(TestCase):
390+
def _check_ldexp(self, tp):
391+
assert_almost_equal(ncu.ldexp(np.array(2., np.float32),
392+
np.array(3, tp)), 16.)
393+
assert_almost_equal(ncu.ldexp(np.array(2., np.float64),
394+
np.array(3, tp)), 16.)
395+
assert_almost_equal(ncu.ldexp(np.array(2., np.longdouble),
396+
np.array(3, tp)), 16.)
397+
390398
def test_ldexp(self):
399+
# The default Python int type should work
391400
assert_almost_equal(ncu.ldexp(2., 3), 16.)
392-
assert_almost_equal(ncu.ldexp(np.array(2., np.float32), np.array(3, np.int16)), 16.)
393-
assert_almost_equal(ncu.ldexp(np.array(2., np.float32), np.array(3, np.int32)), 16.)
394-
assert_almost_equal(ncu.ldexp(np.array(2., np.float64), np.array(3, np.int16)), 16.)
395-
assert_almost_equal(ncu.ldexp(np.array(2., np.float64), np.array(3, np.int32)), 16.)
396-
assert_almost_equal(ncu.ldexp(np.array(2., np.longdouble), np.array(3, np.int16)), 16.)
397-
assert_almost_equal(ncu.ldexp(np.array(2., np.longdouble), np.array(3, np.int32)), 16.)
401+
# The following int types should all be accepted
402+
self._check_ldexp(np.int8)
403+
self._check_ldexp(np.int16)
404+
self._check_ldexp(np.int32)
405+
self._check_ldexp('i')
406+
self._check_ldexp('l')
407+
408+
def test_ldexp_overflow(self):
409+
imax = np.iinfo(np.dtype('l')).max
410+
imin = np.iinfo(np.dtype('l')).min
411+
assert_equal(ncu.ldexp(2., imax), np.inf)
412+
assert_equal(ncu.ldexp(2., imin), 0)
398413

399414

400415
class TestMaximum(TestCase):

0 commit comments

Comments
 (0)