10000 BUG: Fix strange behavior of infinite-step-size/underflow-case in arange by Licht-T · Pull Request #10263 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content

BUG: Fix strange behavior of infinite-step-size/underflow-case in arange #10263

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 17 commits into from
Jun 4, 2018

Conversation

Licht-T
Copy link
Contributor
@Licht-T Licht-T commented Dec 23, 2017

This closes #10206.

The problem is

>>> np.arange(0, 1, 1e10)
array([0.])
>>> np.arange(0, 1, 1e100)
array([0.])
>>> np.arange(0, 1, 1e1000)  # overflows
array([], dtype=float64)
>>> np.arange(0, 1, np.inf)
array([], dtype=float64)

Where the step size becomes infinite.

return -1;

if (val_is_zero && next_is_nonzero) {
if (signbit(value)) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use npy_signbit.

@Licht-T
Copy link
Contributor Author
Licht-T commented Dec 23, 2017

Thanks @xoviat, fixed!

delta = stop - start;
tmp_len = delta/step;

if (tmp_len == 0.0 && delta != 0.0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a comment explaining why it is a special case

Isn't this only satisfied if step is inf anyway?

Copy link
Contributor Author
@Licht-T Licht-T Dec 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eric-wieser This also considers some underflow cases. eg. in master branch,

>>> m = np.float32(3.40282347E+38)
>>> e = np.float32(1.401298E-45)
>>> m
3.4028235e+38
>>> e
1.4012985e-45
>>> e/m
0.0
>>> np.arange(np.float32(0), e, m)
array([], dtype=float64)

@Licht-T
Copy link
Contributor Author
Licht-T commented Dec 24, 2017

@eric-wieser Added comments.

delta = stop - start;
tmp_len = delta/step;

// Underflow and divide-by-inf check
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use C++ comments, use /* ... */.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @charris, fixed!

@Licht-T Licht-T force-pushed the fix-strange-infinite-step-arange branch from 96b261d to d4d2758 Compare December 26, 2017 00:19
if (!val) {
return -1;
}

val_is_zero = PyObject_RichCompareBool(val, zero, Py_EQ);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be an int, not an intp.

@@ -3049,12 +3065,19 @@ _calc_length(PyObject *start, PyObject *stop, PyObject *step, PyObject **next, i
}
return -1;
}

next_is_nonzero = PyObject_RichCompareBool(*next, zero, Py_NE);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use PyObject_IsTrue, but that might be worse on unusual types

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eric-wieser IMO, PyObject_RichCompareBool is better because this is clear what we do.

val = PyNumber_TrueDivide(*next, step);
Py_DECREF(*next);
*next = NULL;

if (!val) {
return -1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zero is leaked here

@Licht-T
Copy link
Contributor Author
Licht-T commented Dec 27, 2017

Thanks @eric-wieser, fixed!

@@ -3049,12 +3066,20 @@ _calc_length(PyObject *start, PyObject *stop, PyObject *step, PyObject **next, i
}
return -1;
Copy link
Member
@eric-wieser < 8000 strong> eric-wieser Dec 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to DECREF zero here too - or better yet, just allocate it further down.

@@ -3036,7 +3051,9 @@ static npy_intp
_calc_length(PyObject *start, PyObject *stop, PyObject *step, PyObject **next, int cmplx)
{
npy_intp len, tmp;
PyObject *zero = PyInt_FromLong(0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really you should check the return value is not NULL here too.

@Licht-T Licht-T force-pushed the fix-strange-infinite-step-arange branch from 5f6b735 to 41cd4e1 Compare December 28, 2017 00:32
@Licht-T
Copy link
Contributor Author
Licht-T commented Dec 28, 2017

@eric-wieser Fixed!

@Licht-T Licht-T changed the title BUG: Fix strange behavior of infinite step size in arange BUG: Fix strange behavior of infinite-step-size/underflow-case in arange Dec 28, 2017
@Licht-T
Copy link
Contributor Author
Licht-T commented Dec 28, 2017

Also, I need to add some underflow-case tests.

*next = PyNumber_Subtract(stop, start);
if (!(*next)) {
Py_DECREF(zero);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you move the allocation of zero down below this if, then you don't need to clear it up here

Copy link
Contributor Author
@Licht-T Licht-T Dec 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eric-wieser If I move down zero allocation, we need to DECREF *next and set NULL to *next when the zero allocation failed. IMO, this is complicated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and set NULL to *next when the zero allocation failed.

You already need to do this

@eric-wieser
Copy link
Member

Did you mean to refer to #10271 in the PR description? Seems unrelated to me

@Licht-T
Copy link
Contributor Author
Licht-T commented Dec 28, 2017

@eric-wieser I did not refer to that issue. Maybe @charris added this.

Did you mean to refer to #10271 in the PR description? Seems unrelated to me

@Licht-T
Copy link
Contributor Author
Licht-T commented Dec 29, 2017

Thanks @eric-wieser, fixed!

@Licht-T
Copy link
Contributor Author
Licht-T commented Dec 29, 2017

Added the test for some underflow cases


val_is_zero = PyObject_RichCompareBool(val, zero, Py_EQ);
Py_DECREF(zero);

if (cmplx && PyComplex_Check(val)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying to work out if this section needs a fix too, but I can't work out what this is even trying to do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eric-wieser Me t 10000 oo. I don't know why the complex case exists. This is none-sense as math.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #10332.

@eric-wieser
Copy link
Member

Can you extract double _ceildiv(double, double) and PyObject * _ceildiv_O(PyObject *, PyObject *) helper functions here, that handles all these corner cases for us?

eric-wieser
eric-wieser previously approved these changes Jan 6, 2018
Copy link
Member
@eric-wieser eric-wieser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second thoughts, this is fine as is - I'll leave it open for a few more days to let someone else double-check refcounts, and then squash and merge your commits.

length = -1.0;
}
else {
length = 1.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should be 1, as this is an integer, not double, variable. Same above.

len = -1.0;
}
else {
len = 1.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here for int vs double

/* Underflow and divide-by-inf check */
if (val_is_zero && next_is_nonzero) {
if (npy_signbit(value)) {
len = -1.0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be 0. as it is effectively ceil(-eps)

@eric-wieser eric-wieser dismissed their stale review April 11, 2018 06:44

See above comments - looks like I missed a couple things when I hit approve

@Licht-T
Copy link
Contributor Author
Licht-T commented May 2, 2018

@eric-wieser This completely slipped my mind! I'm back, and it will be fixed in a couple of days.

@Licht-T
Copy link
Contributor Author
Licht-T commented May 2, 2018

@eric-wieser Fixed, and now all green!

return -1;
}

val_is_zero = PyObject_RichCompareBool(val, zero, Py_EQ);
Copy link
Member
@eric-wieser eric-wieser May 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to check that this doesn't return -1, indicating an error occurred. Same above.

@Licht-T
Copy link
Contributor Author
Licht-T commented May 4, 2018

Thanks @eric-wieser, fixed!

@eric-wieser
Copy link
Member

Minor style nits, but easiest to fix them myself. Thanks for the perseverance - I'll leave this for a couple days to see if there are any more comments

@charris
Copy link
Member
charris commented Jun 2, 2018

@eric-wieser Just a ping.

@eric-wieser eric-wieser merged commit abeb96d into numpy:master Jun 4, 2018
@eric-wieser
Copy link
Member

Thanks for the perseverance, @Licht-T - this will be in 1.15

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

BUG: Strange behavior of infinite step size in arange
3 participants
0