-
-
Notifications
You must be signed in to change notification settings - Fork 11.1k
BUG: Fix casting from longdouble to long #9971
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
Conversation
1821d0c
to
922fcb6
Compare
a1830b5
to
f10d9d3
Compare
#9967 is in and needs rebase. |
/* Get the MSBs of the integral part of the float */ | ||
frac = npy_ldexpl(frac, (expo-1) % chunk_size + 1); | ||
for (i = ndig; --i >= 0; ) { | ||
npy_int32 chunk = (npy_int32)frac; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not npy_uint64
with chunk_size
64?
digits
in the original code was unsigned
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hadn't noticed that it was unsigned. Yes, could probably change to sizeof(npy_long) * CHAR_BIT
and npy_long
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to use PyLong_FromUnsignedLongLong
, npy_ulonglong
, and NPY_BITSOF_ULONGLONG
if (v == NULL) { | ||
goto done; | ||
} | ||
l_chunk = PyLong_FromLong(chunk); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, maybe the reason for 32 bit chunk is because npy_long
can be 32bit, and chunk
needs to be npy_long
.
done: | ||
Py_DECREF(l_chunk_size); | ||
return v; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a funny character here. Windows newline? OK, it says "No newline at end of file", so no problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Newline added
7d8004c
to
ec36b2b
Compare
* Notably, we can't set the digits directly, so have to shift and or instead. | ||
*/ | ||
PyObject * | ||
PyLong_FromLongDouble(npy_longdouble ldval) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternate name proposal npy_longdouble_to_PyLong
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either seems fine. Maybe the npy_
alternative is safer in case Python3 decides to define this too.
#if @realtyp@ | ||
double ix; | ||
modf(x, &ix); | ||
x = ix; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seemed to be just for the long
/int
choice below, which is far easier (although a little slower) to handle by just producing a long
, and letting python decide whether to downcast to an int.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can now remove the @realtype@
repeat var. @unsigntyp@
too
(I don't exactly understand why these lines were needed before, since C-casting truncates the double fractional part anyway.. but the new code looks correct so I won't worry too much).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I think it was probably so the x < LONG_MAX
and x < LONG_MIN
checks aren't affected by the fractional value, in the case of exact equality of the integer part of x
to those values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, that was the purpose - but I don't think the decrease in clarity is worth it for a marginal performance improvement on only python 2.
I'll remove the repeat vars - good catch
PyObject *_py_tmp = (PyObject *)(op); \ | ||
(op) = (op2); \ | ||
Py_DECREF(_py_tmp); \ | ||
} while (0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would probably be generally handy throughout the codebase.
A job for another PR: would there be a better place to put it, and do we want to cal lit Py_SETREF
and #ifndef Py_SETREF
it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we decide to use it I agree that doing and #ifndef
seems reasonable.
For anyone else reading, it looks like this python macro was created in python3.4, and there are a lot of discussions of it on the python mailing list and on the python issue tracker. It is still going through some changes as of april 2016, I think they decided to split it into two macros Py_SETREF
and Py_XSETREF
.
One motivation for it is to avoid a certain kind of bug, according to the comment in CPython. On the other hand it's not part of the "stable api", so there are some risks in using it.
ec36b2b
to
3a60d85
Compare
Besides the small cleanup mentioned above, LGTM. |
Implements a custom PyLong_FromLongDouble (with a different name) derived from PyLong_FromDouble. Fixes numpygh-9964
3a60d85
to
e0fb739
Compare
Cleanup made, used the non-clashing name |
LGTM, merging. Thanks Eric! |
Seems to fail the wheels build on the mac, https://travis-ci.org/MacPython/numpy-wheels. EDIT: It's not obvious what the problem is, the Mac does support extended precision long doubles. Maybe a library function (clang vs gcc) or some such ... |
I think this indicates that we need to add Mac testing. |
{ | ||
PyObject *v; | ||
PyObject *l_chunk_size; | ||
// number of bits to extract at a time. CPython uses 30, but that's because |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, should be C style comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Careless, good spot. This is the reason for the Mac failure?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. At the moment I'm suspecting the power function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is this line: huge_ld = 1234 * np.longdouble(2) ** exp
. We need someone running on a Mac to check this out. Maybe a fallback double function is being used somewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it is, powl
is blacklisted on the Mac
/* powl gives zero division warning on OS X, see gh-8307 */
#if defined(HAVE_POWL) && defined(NPY_OS_DARWIN)
#undef HAVE_POWL
#endif
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although I don't see why that should be a problem, the double value of the fallback function should be OK with result cast to long double
, so the multiplication should work. Maybe inlining is failing somewhere ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fired up the old Mac. The Mac double pow
function errors with exp == 1024
, Linux works with that. I'm simply going to use exp = 1023
as a fix in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exp=1023 doesn't work, because then the result fits in double
- the goal here is to produce a longdouble that exceeds the limits for a double.
I suppose using exp=1023 and multiplying by 2 would work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that is what I did. See #10000 . Oh, looky there, issue 10000, it's like watching the odometer turn over ...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guess we will eventually find out if this all works for IBM double double.
@@ -419,6 +420,21 @@ def test_clongdouble___int__(self): | |||
assert_raises(OverflowError, x.__int__) | |||
assert_equal(len(sup.log), 1) | |||
|
|||
@dec.skipif(np.finfo(np.double) == np.finfo(np.longdouble)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test fails on ppc64.
I think I'm going to add @dec.skipif(platform.machine().startswith("ppc64"))
here.
Implements a custom PyLong_FromLongDouble derived from PyLong_FromDouble.
Fixes gh-9964
Depends on #9967 being merged, but submitting now to allow tests to run on a platform with longdouble support, something mine does not have. I'll rebase once that's merged.