8000 BUG: core: implement a long-int loop for ldexp, for cases where int !… · numpy/numpy@93f7521 · GitHub
[go: up one dir, main page]

Skip to content

Commit 93f7521

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.
1 parent d24db34 commit 93f7521

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
@@ -1286,6 +1286,36 @@ NPY_NO_EXPORT void
12861286
*((@type@ *)op1) = ldexp@c@(in1, in2);
12871287
}
12881288
}
1289+
1290+
NPY_NO_EXPORT void
1291+
@TYPE@_ldexp_long(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func))
1292+
{
1293+
/*
1294+
* Additional loop to handle long integer inputs (cf. #866, #1633).
1295+
* long != int on many 64-bit platforms, so we need this second loop
1296+
* to handle the default integer type.
1297+
*/
1298+
BINARY_LOOP {
1299+
const @type@ in1 = *(@type@ *)ip1;
1300+
const long in2 = *(long *)ip2;
1301+
if (((int)in2) == in2) {
1302+
/* Range OK */
1303+
*((@type@ *)op1) = ldexp@c@(in1, ((int)in2));
1304+
}
1305+
else {
1306+
/*
1307+
* Outside int range -- also ldexp will overflow in this case,
1308+
* given that exponent has less bits than int.
1309+
*/
1310+
if (in2 > 0) {
1311+
*((@type@ *)op1) = ldexp@c@(in1, NPY_MAX_INT);
1312+
}
1313+
else {
1314+
*((@type@ *)op1) = ldexp@c@(in1, NPY_MIN_INT);
1315+
}
1316+
}
1317+
}
1318+
}
12891319
#endif
12901320

12911321
#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
@@ -379,14 +379,29 @@ def test_nan_any(self):
379379

380380

381381
class TestLdexp(TestCase):
382+
def _check_ldexp(self, tp):
383+
assert_almost_equal(ncu.ldexp(np.array(2., np.float32),
384+
np.array(3, tp)), 16.)
385+
assert_almost_equal(ncu.ldexp(np.array(2., np.float64),
386+
np.array(3, tp)), 16.)
387+
assert_almost_equal(ncu.ldexp(np.array(2., np.longdouble),
388+
np.array(3, tp)), 16.)
389+
382390
def test_ldexp(self):
391+
# The default Python int type should work
383392
assert_almost_equal(ncu.ldexp(2., 3), 16.)
384-
assert_almost_equal(ncu.ldexp(np.array(2., np.float32), np.array(3, np.int16)), 16.)
385-
assert_almost_equal(ncu.ldexp(np.array(2., np.float32), np.array(3, np.int32)), 16.)
386-
assert_almost_equal(ncu.ldexp(np.array(2., np.float64), np.array(3, np.int16)), 16.)
387-
assert_almost_equal(ncu.ldexp(np.array(2., np.float64), np.array(3, np.int32)), 16.)
388-
assert_almost_equal(ncu.ldexp(np.array(2., np.longdouble), np.array(3, np.int16)), 16.)
389-
assert_almost_equal(ncu.ldexp(np.array(2., np.longdouble), np.array(3, np.int32)), 16.)
393+
# The following int types should all be accepted
394+
self._check_ldexp(np.int8)
395+
self._check_ldexp(np.int16)
396+
self._check_ldexp(np.int32)
397+
self._check_ldexp('i')
398+
self._check_ldexp('l')
399+
400+
def test_ldexp_overflow(self):
401+
imax = np.iinfo(np.dtype('l')).max
402+
imin = np.iinfo(np.dtype('l')).min
403+
assert_equal(ncu.ldexp(2., imax), np.inf)
404+
assert_equal(ncu.ldexp(2., imin), 0)
390405

391406

392407
class TestMaximum(TestCase):

0 commit comments

Comments
 (0)
0