1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
|
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#define _MULTIARRAYMODULE
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "numpy/ndarraytypes.h"
#include "numpy/npy_math.h"
#include "npy_pycompat.h"
#include "numpyos.h"
/*
* Heavily derived from PyLong_FromDouble
* Notably, we can't set the digits directly, so have to shift and or instead.
*/
NPY_VISIBILITY_HIDDEN PyObject *
npy_longdouble_to_PyLong(npy_longdouble ldval)
{
PyObject *v;
PyObject *l_chunk_size;
/*
* number of bits to extract at a time. CPython uses 30, but that's because
* it's tied to the internal long representation
*/
const int chunk_size = NPY_BITSOF_LONGLONG;
npy_longdouble frac;
int i, ndig, expo, neg;
neg = 0;
if (npy_isinf(ldval)) {
PyErr_SetString(PyExc_OverflowError,
"cannot convert longdouble infinity to integer");
return NULL;
}
if (npy_isnan(ldval)) {
PyErr_SetString(PyExc_ValueError,
"cannot convert longdouble NaN to integer");
return NULL;
}
if (ldval < 0.0) {
neg = 1;
ldval = -ldval;
}
frac = npy_frexpl(ldval, &expo); /* ldval = frac*2**expo; 0.0 <= frac < 1.0 */
v = PyLong_FromLong(0L);
if (v == NULL)
return NULL;
if (expo <= 0)
return v;
ndig = (expo-1) / chunk_size + 1;
l_chunk_size = PyLong_FromLong(chunk_size);
if (l_chunk_size == NULL) {
Py_DECREF(v);
return NULL;
}
/* 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_ulonglong chunk = (npy_ulonglong)frac;
PyObject *l_chunk;
/* v = v << chunk_size */
Py_SETREF(v, PyNumber_Lshift(v, l_chunk_size));
if (v == NULL) {
goto done;
}
l_chunk = PyLong_FromUnsignedLongLong(chunk);
if (l_chunk == NULL) {
Py_DECREF(v);
v = NULL;
goto done;
}
/* v = v | chunk */
Py_SETREF(v, PyNumber_Or(v, l_chunk));
Py_DECREF(l_chunk);
if (v == NULL) {
goto done;
}
/* Remove the msbs, and repeat */
frac = frac - (npy_longdouble) chunk;
frac = npy_ldexpl(frac, chunk_size);
}
/* v = -v */
if (neg) {
Py_SETREF(v, PyNumber_Negative(v));
if (v == NULL) {
goto done;
}
}
done:
Py_DECREF(l_chunk_size);
return v;
}
/* Helper function to get unicode(PyLong).encode('utf8') */
static PyObject *
_PyLong_Bytes(PyObject *long_obj) {
PyObject *bytes;
PyObject *unicode = PyObject_Str(long_obj);
if (unicode == NULL) {
return NULL;
}
bytes = PyUnicode_AsUTF8String(unicode);
Py_DECREF(unicode);
return bytes;
}
/**
* TODO: currently a hack that converts the long through a string. This is
* correct, but slow.
*
* Another approach would be to do this numerically, in a similar way to
* PyLong_AsDouble.
* However, in order to respect rounding modes correctly, this needs to know
* the size of the mantissa, which is platform-dependent.
*/
NPY_VISIBILITY_HIDDEN npy_longdouble
npy_longdouble_from_PyLong(PyObject *long_obj) {
npy_longdouble result = 1234;
char *end;
char *cstr;
PyObject *bytes;
/* convert the long to a string */
bytes = _PyLong_Bytes(long_obj);
if (bytes == NULL) {
return -1;
}
cstr = PyBytes_AsString(bytes);
if (cstr == NULL) {
goto fail;
}
end = NULL;
/* convert the string to a long double and capture errors */
errno = 0;
result = NumPyOS_ascii_strtold(cstr, &end);
if (errno == ERANGE) {
/* strtold returns INFINITY of the correct sign. */
if (PyErr_Warn(PyExc_RuntimeWarning,
"overflow encountered in conversion from python long") < 0) {
goto fail;
}
}
else if (errno) {
PyErr_Format(PyExc_RuntimeError,
"Could not parse python long as longdouble: %s (%s)",
cstr,
strerror(errno));
goto fail;
}
/* Extra characters at the end of the string, or nothing parsed */
if (end == cstr || *end != '\0') {
PyErr_Format(PyExc_RuntimeError,
"Could not parse long as longdouble: %s",
cstr);
goto fail;
}
/* finally safe to decref now that we're done with `end` */
Py_DECREF(bytes);
return result;
fail:
Py_DECREF(bytes);
return -1;
}
|