8000 BUG: Prevent underflow causing empty result in arange (#10263) · numpy/numpy@abeb96d · GitHub
[go: up one dir, main page]

Skip to content

Commit abeb96d

Browse files
Licht-Teric-wieser
authored andcommitted
BUG: Prevent underflow causing empty result in arange (#10263)
When given a step size larger than the difference between `start` and `stop`, arange would sometimes give `[]` and sometimes `[start]`, depending on the relative magnitudes. Now the result is always `[start]`, even if the step is infinite, or underflow occurs. Fixes #10206
1 parent 2e60274 commit abeb96d

File tree

2 files changed

+97
-7
lines changed

2 files changed

+97
-7
lines changed

numpy/core/src/multiarray/ctors.c

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2999,11 +2999,26 @@ PyArray_Arange(double start, double stop, double step, int type_num)
29992999
PyArray_ArrFuncs *funcs;
30003000
PyObject *obj;
30013001
int ret;
3002+
double delta, tmp_len;
30023003
NPY_BEGIN_THREADS_DEF;
30033004

3004-
length = _arange_safe_ceil_to_intp((stop - start)/step);
3005-
if (error_converting(length)) {
3006-
return NULL;
3005+
delta = stop - start;
3006+
tmp_len = delta/step;
3007+
3008+
/* Underflow and divide-by-inf check */
3009+
if (tmp_len == 0.0 && delta != 0.0) {
3010+
if (npy_signbit(tmp_len)) {
3011+
length = 0;
3012+
}
3013+
else {
3014+
length = 1;
3015+
}
3016+
}
3017+
else {
3018+
length = _arange_safe_ceil_to_intp(tmp_len);
3019+
if (error_converting(length)) {
3020+
return NULL;
3021+
}
30073022
}
30083023

30093024
if (length <= 0) {
@@ -3067,7 +3082,8 @@ static npy_intp
30673082
_calc_length(PyObject *start, PyObject *stop, PyObject *step, PyObject **next, int cmplx)
30683083
{
30693084
npy_intp len, tmp;
3070-
PyObject *val;
3085+
PyObject *zero, *val;
3086+
int next_is_nonzero, val_is_zero;
30713087
double value;
30723088

30733089
*next = PyNumber_Subtract(stop, start);
@@ -3080,12 +3096,37 @@ _calc_length(PyObject *start, PyObject *stop, PyObject *step, PyObject **next, i
30803096
}
30813097
return -1;
30823098
}
3099+
3100+
zero = PyInt_FromLong(0);
3101+
if (!zero) {
3102+
Py_DECREF(*next);
3103+
*next = NULL;
3104+
return -1;
3105+
}
3106+
3107+
next_is_nonzero = PyObject_RichCompareBool(*next, zero, Py_NE);
3108+
if (next_is_nonzero == -1) {
3109+
Py_DECREF(zero);
3110+
Py_DECREF(*next);
3111+
*next = NULL;
3112+
return -1;
3113+
}
30833114
val = PyNumber_TrueDivide(*next, step);
30843115
Py_DECREF(*next);
30853116
*next = NULL;
3117+
30863118
if (!val) {
3119+
Py_DECREF(zero);
30873120
return -1;
30883121
}
3122+
3123+
val_is_zero = PyObject_RichCompareBool(val, zero, Py_EQ);
3124+
Py_DECREF(zero);
3125+
if (val_is_zero == -1) {
3126+
Py_DECREF(val);
3127+
return -1;
3128+
}
3129+
30893130
if (cmplx && PyComplex_Check(val)) {
30903131
value = PyComplex_RealAsDouble(val);
30913132
if (error_converting(value)) {
@@ -3114,11 +3155,24 @@ _calc_length(PyObject *start, PyObject *stop, PyObject *step, PyObject **next, i
31143155
if (error_converting(value)) {
31153156
return -1;
31163157
}
3117-
len = _arange_safe_ceil_to_intp(value);
3118-
if (error_converting(len)) {
3119-
return -1;
3158+
3159+
/* Underflow and divide-by-inf check */
3160+
if (val_is_zero && next_is_nonzero) {
3161+
if (npy_signbit(value)) {
3162+
len = 0;
3163+
}
3164+
else {
3165+
len = 1;
3166+
}
3167+
}
3168+
else {
3169+
len = _arange_safe_ceil_to_intp(value);
3170+
if (error_converting(len)) {
3171+
return -1;
3172+
}
31203173
}
31213174
}
3175+
31223176
if (len > 0) {
31233177
*next = PyNumber_Add(start, step);
31243178
if (!*next) {

numpy/core/tests/test_regression.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,42 @@ def test_arange_endian(self):
224224
x = np.arange(10, dtype='>f8')
225225
assert_array_equal(ref, x)
226226

227+
def test_arange_inf_step(self):
228+
ref = np.arange(0, 1, 10)
229+
x = np.arange(0, 1, np.inf)
230+
assert_array_equal(ref, x)
231+
232+
ref = np.arange(0, 1, -10)
233+
x = np.arange(0, 1, -np.inf)
234+
assert_array_equal(ref, x)
235+
236+
ref = np.arange(0, -1, -10)
237+
x = np.arange(0, -1, -np.inf)
238+
assert_array_equal(ref, x)
239+
240+
ref = np.arange(0, -1, 10)
241+
x = np.arange(0, -1, np.inf)
242+
assert_array_equal(ref, x)
243+
244+
def test_arange_underflow_stop_and_step(self):
245+
finfo = np.finfo(np.float64)
246+
247+
ref = np.arange(0, finfo.eps, 2 * finfo.eps)
248+
x = np.arange(0, finfo.eps, finfo.max)
249+
assert_array_equal(ref, x)
250+
251+
ref = np.arange(0, finfo.eps, -2 * finfo.eps)
252+
x = np.arange(0, finfo.eps, -finfo.max)
253+
assert_array_equal(ref, x)
254+
255+
ref = np.arange(0, -finfo.eps, -2 * finfo.eps)
256+
x = np.arange(0, -finfo.eps, -finfo.max)
257+
assert_array_equal(ref, x)
258+
259+
ref = np.arange(0, -finfo.eps, 2 * finfo.eps)
260+
x = np.arange(0, -finfo.eps, finfo.max)
261+
assert_array_equal(ref, x)
262+
227263
def test_argmax(self):
228264
# Ticket #119
229265
a = np.random.normal(0, 1, (4, 5, 6, 7, 8))

0 commit comments

Comments
 (0)
0