1
#include <Python.h>
2

3
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
4
#define _MULTIARRAYMODULE
5

6
#include "numpy/ndarraytypes.h"
7
#include "numpy/npy_math.h"
8
#include "npy_pycompat.h"
9
#include "numpyos.h"
10

11
/*
12
 * Heavily derived from PyLong_FromDouble
13
 * Notably, we can't set the digits directly, so have to shift and or instead.
14
 */
15
NPY_VISIBILITY_HIDDEN PyObject *
16 1
npy_longdouble_to_PyLong(npy_longdouble ldval)
17
{
18
    PyObject *v;
19
    PyObject *l_chunk_size;
20
    /*
21
     * number of bits to extract at a time. CPython uses 30, but that's because
22
     * it's tied to the internal long representation
23
     */
24 1
    const int chunk_size = NPY_BITSOF_LONGLONG;
25
    npy_longdouble frac;
26
    int i, ndig, expo, neg;
27 1
    neg = 0;
28

29 1
    if (npy_isinf(ldval)) {
30 1
        PyErr_SetString(PyExc_OverflowError,
31
                        "cannot convert longdouble infinity to integer");
32 1
        return NULL;
33
    }
34 1
    if (npy_isnan(ldval)) {
35 0
        PyErr_SetString(PyExc_ValueError,
36
                        "cannot convert longdouble NaN to integer");
37 0
        return NULL;
38
    }
39 1
    if (ldval < 0.0) {
40 1
        neg = 1;
41 1
        ldval = -ldval;
42
    }
43 1
    frac = npy_frexpl(ldval, &expo); /* ldval = frac*2**expo; 0.0 <= frac < 1.0 */
44 1
    v = PyLong_FromLong(0L);
45 1
    if (v == NULL)
46
        return NULL;
47 1
    if (expo <= 0)
48
        return v;
49

50 1
    ndig = (expo-1) / chunk_size + 1;
51

52 1
    l_chunk_size = PyLong_FromLong(chunk_size);
53 1
    if (l_chunk_size == NULL) {
54 0
        Py_DECREF(v);
55
        return NULL;
56
    }
57

58
    /* Get the MSBs of the integral part of the float */
59 1
    frac = npy_ldexpl(frac, (expo-1) % chunk_size + 1);
60 1
    for (i = ndig; --i >= 0; ) {
61 1
        npy_ulonglong chunk = (npy_ulonglong)frac;
62
        PyObject *l_chunk;
63
        /* v = v << chunk_size */
64 1
        Py_SETREF(v, PyNumber_Lshift(v, l_chunk_size));
65 1
        if (v == NULL) {
66
            goto done;
67
        }
68 1
        l_chunk = PyLong_FromUnsignedLongLong(chunk);
69 1
        if (l_chunk == NULL) {
70 0
            Py_DECREF(v);
71
            v = NULL;
72
            goto done;
73
        }
74
        /* v = v | chunk */
75 1
        Py_SETREF(v, PyNumber_Or(v, l_chunk));
76 1
        Py_DECREF(l_chunk);
77 1
        if (v == NULL) {
78
            goto done;
79
        }
80

81
        /* Remove the msbs, and repeat */
82 1
        frac = frac - (npy_longdouble) chunk;
83 1
        frac = npy_ldexpl(frac, chunk_size);
84
    }
85

86
    /* v = -v */
87 1
    if (neg) {
88 1
        Py_SETREF(v, PyNumber_Negative(v));
89
        if (v == NULL) {
90
            goto done;
91
        }
92
    }
93

94 1
done:
95 1
    Py_DECREF(l_chunk_size);
96
    return v;
97
}
98

99
/* Helper function to get unicode(PyLong).encode('utf8') */
100
static PyObject *
101 1
_PyLong_Bytes(PyObject *long_obj) {
102
    PyObject *bytes;
103 1
    PyObject *unicode = PyObject_Str(long_obj);
104 1
    if (unicode == NULL) {
105
        return NULL;
106
    }
107 1
    bytes = PyUnicode_AsUTF8String(unicode);
108 1
    Py_DECREF(unicode);
109
    return bytes;
110
}
111

112

113
/**
114
 * TODO: currently a hack that converts the long through a string. This is
115
 * correct, but slow.
116
 *
117
 * Another approach would be to do this numerically, in a similar way to
118
 * PyLong_AsDouble.
119
 * However, in order to respect rounding modes correctly, this needs to know
120
 * the size of the mantissa, which is platform-dependent.
121
 */
122
NPY_VISIBILITY_HIDDEN npy_longdouble
123 1
npy_longdouble_from_PyLong(PyObject *long_obj) {
124 1
    npy_longdouble result = 1234;
125
    char *end;
126
    char *cstr;
127
    PyObject *bytes;
128

129
    /* convert the long to a string */
130 1
    bytes = _PyLong_Bytes(long_obj);
131 1
    if (bytes == NULL) {
132
        return -1;
133
    }
134

135 1
    cstr = PyBytes_AsString(bytes);
136 1
    if (cstr == NULL) {
137
        goto fail;
138
    }
139 1
    end = NULL;
140

141
    /* convert the string to a long double and capture errors */
142 1
    errno = 0;
143 1
    result = NumPyOS_ascii_strtold(cstr, &end);
144 1
    if (errno == ERANGE) {
145
        /* strtold returns INFINITY of the correct sign. */
146 0
        if (PyErr_Warn(PyExc_RuntimeWarning,
147
                "overflow encountered in conversion from python long") < 0) {
148
            goto fail;
149
        }
150
    }
151 1
    else if (errno) {
152 0
        PyErr_Format(PyExc_RuntimeError,
153
                     "Could not parse python long as longdouble: %s (%s)",
154
                     cstr,
155
                     strerror(errno));
156 0
        goto fail;
157
    }
158

159
    /* Extra characters at the end of the string, or nothing parsed */
160 1
    if (end == cstr || *end != '\0') {
161 0
        PyErr_Format(PyExc_RuntimeError,
162
                     "Could not parse long as longdouble: %s",
163
                     cstr);
164 0
        goto fail;
165
    }
166

167
    /* finally safe to decref now that we're done with `end` */
168 1
    Py_DECREF(bytes);
169
    return result;
170

171 0
fail:
172 0
    Py_DECREF(bytes);
173
    return -1;
174
}

Read our documentation on viewing source code .

Loading