8000 BUG: longdouble(int) does not work · numpy/numpy@3d07480 · GitHub
[go: up one dir, main page]

Skip to content

Commit 3d07480

Browse files
eric-wiesercharris
authored andcommitted
BUG: longdouble(int) does not work
Fixes gh-9968
1 parent 1b5b0b4 commit 3d07480

File tree

3 files changed

+104
-0
lines changed

3 files changed

+104
-0
lines changed

numpy/core/src/common/npy_longdouble.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "numpy/ndarraytypes.h"
77
#include "numpy/npy_math.h"
88
#include "npy_pycompat.h"
9+
#include "numpyos.h"
910

1011
/*
1112
* Heavily derived from PyLong_FromDouble
@@ -94,3 +95,85 @@ npy_longdouble_to_PyLong(npy_longdouble ldval)
9495
Py_DECREF(l_chunk_size);
9596
return v;
9697
}
98+
99+
/* Helper function to get unicode(PyLong).encode('utf8') */
100+
static PyObject *
101+
_PyLong_Bytes(PyObject *long_obj) {
102+
PyObject *bytes;
103+
#if defined(NPY_PY3K)
104+
PyObject *unicode = PyObject_Str(long_obj);
105+
if (unicode == NULL) {
106+
return NULL;
107+
}
108+
bytes = PyUnicode_AsUTF8String(unicode);
109+
Py_DECREF(unicode);
110+
#else
111+
bytes = PyObject_Str(long_obj);
112+
#endif
113+
return bytes;
114+
}
115+
116+
117+
/**
118+
* TODO: currently a hack that converts the long through a string. This is
119+
* correct, but slow.
120+
*
121+
* Another approach would be to do this numerically, in a similar way to
122+
* PyLong_AsDouble.
123+
* However, in order to respect rounding modes correctly, this needs to know
124+
* the size of the mantissa, which is platform-dependent.
125+
*/
126+
NPY_VISIBILITY_HIDDEN npy_longdouble
127+
npy_longdouble_from_PyLong(PyObject *long_obj) {
128+
npy_longdouble result = 1234;
129+
char *end;
130+
char *cstr;
131+
PyObject *bytes;
132+
133+
/* convert the long to a string */
134+
bytes = _PyLong_Bytes(long_obj);
135+
if (bytes == NULL) {
136+
return -1;
137+
}
138+
139+
cstr = PyBytes_AsString(bytes);
140+
if (cstr == NULL) {
141+
Py_DECREF(bytes);
142+
return -1;
143+
}
144+
end = NULL;
145+
146+
/* convert the string to a long double and capture errors */
147+
errno = 0;
148+
result = NumPyOS_ascii_strtold(cstr, &end);
149+
int errno_save = errno;
150+
151+
Py_DECREF(bytes);
152+
if (errno_save == ERANGE) {
153+
/* strtold returns INFINITY of the correct sign. */
154+
if (PyErr_Warn(PyExc_RuntimeWarning,
155+
"overflow encountered in conversion from python long") < 0) {
156+
return -1;
157+
}
158+
}
159+
else if (errno_save) {
160+
PyErr_Format(PyExc_RuntimeError,
161+
"Could not parse python long as longdouble: %s (%s)",
162+
cstr,
163+
strerror(errno_save));
164+
return -1;
165+
}
166+
167+
/* Extra characters at the end of the string, or nothing parsed */
168+
if (end == cstr || *end) {
169+
PyErr_Format(PyExc_RuntimeError,
170+
"Could not parse long as longdouble: %s",
171+
cstr);
172+
return -1;
173+
}
174+
175+
// Without this line, MSVC produces garbage (optimizes out result!?)
176+
printf("Double form is %f\n", (double) result);
177+
178+
return result;
179+
}

numpy/core/src/common/npy_longdouble.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,14 @@
1414
NPY_VISIBILITY_HIDDEN PyObject *
1515
npy_longdouble_to_PyLong(npy_longdouble ldval);
1616

17+
/* Convert a python `long` integer to a npy_longdouble
18+
*
19+
* This performs the same task as PyLong_AsDouble, but for long doubles
20+
* which have a greater range.
21+
*
22+
* Returns -1 if an error occurs.
23+
*/
24+
NPY_VISIBILITY_HIDDEN npy_longdouble
25+
npy_longdouble_from_PyLong(PyObject *long_obj);
26+
1727
#endif

numpy/core/src/multiarray/arraytypes.c.src

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,17 @@ string_to_long_double(PyObject*op)
328328
npy_longdouble temp;
329329
PyObject* b;
330330

331+
/* Convert python long objects to a longdouble, without precision or range
332+
* loss via a double.
333+
*/
334+
if (PyLong_Check(op)
335+
#if !defined(NPY_PY3K)
336+
|| PyInt_Check(op)
337+
#endif
338+
) {
339+
return npy_longdouble_from_PyLong(op);
340+
}
341+
331342
if (PyUnicode_Check(op)) {
332343
b = PyUnicode_AsUTF8String(op);
333344
if (!b) {

0 commit comments

Comments
 (0)
0