1 /*
2 * Functions that will convert/evaluate a Python number.
3 *
4 * Author: Seth M. Morton
5 *
6 * July 2018
7 */
8
9 #include <Python.h>
10 #include <float.h>
11 #include "fastnumbers/numbers.h"
12 #include "fastnumbers/options.h"
13 #include "fastnumbers/pstdint.h"
14
15
16 /* Ensure 64 bits are handled. */
17 #ifndef INT64_MAX
18 #error "fastnumbers requires that your compiler support 64 bit integers, but it appears that this compiler does not"
19 #endif
20
21
22 /*
23 * Copy of _Py_dg_stdnan from the Python code base.
24 * Dynamically generate an NaN. Needed for Windows.
25 * I hate copy/pasting code, but this seems like the only way.
26 */
27 typedef union {
28 double d;
29 uint32_t L[2];
30 } U;
31 #ifdef DOUBLE_IS_LITTLE_ENDIAN_IEEE754
32 #define word0(x) (x)->L[1]
33 #define word1(x) (x)->L[0]
34 #else
35 #define word0(x) (x)->L[0]
36 #define word1(x) (x)->L[1]
37 #endif
38 #define dval(x) (x)->d
39 #define NAN_WORD0 0x7ff80000
40 #define NAN_WORD1 0
41 #define Sign_bit 0x80000000
42 double
compat_generate_nan(int sign)43 compat_generate_nan(int sign) {
44 U rv;
45 word0(&rv) = NAN_WORD0;
46 word1(&rv) = NAN_WORD1;
47 if (sign) {
48 word0(&rv) |= Sign_bit;
49 }
50 return dval(&rv);
51 }
52
53
54 static bool
_PyFloat_is_Intlike(PyObject * obj)55 _PyFloat_is_Intlike(PyObject *obj) {
56 /* NOTE: This code copy/pasted with modification from the CPython source.
57 They did not expose it as public for some reason.
58 */
59 bool retval = false;
60 const double x = PyFloat_AsDouble(obj);
61
62 if (x == -1.0 && PyErr_Occurred()) {
63 return PyErr_Clear(), false;
64 }
65 if (!Py_IS_FINITE(x)) {
66 return false;
67 }
68 errno = 0;
69 retval = (floor(x) == x) ? true : false;
70 if (errno != 0) {
71 return false;
72 }
73 return retval;
74 }
75
76
77 static PyObject *
PyNumber_to_PyInt_or_PyFloat(PyObject * pynum,const Options * options)78 PyNumber_to_PyInt_or_PyFloat(PyObject *pynum, const Options *options)
79 {
80 if (Options_Has_NaN_Sub(options) && PyNumber_IsNAN(pynum)) {
81 return Options_Return_NaN_Sub(options);
82 }
83 else if (Options_Has_INF_Sub(options) && PyNumber_IsINF(pynum)) {
84 return Options_Return_INF_Sub(options);
85 }
86 else if (Options_Coerce_True(options)) {
87 if (PyLong_Check(pynum) || PyFloat_is_Intlike(pynum)) {
88 return PyNumber_Long(pynum);
89 }
90 else {
91 return PyNumber_Float(pynum);
92 }
93 }
94 else {
95 return Py_INCREF(pynum), pynum;
96 }
97 }
98
99
100 static PyObject *
PyNumber_to_PyFloat(PyObject * pynum,const Options * options)101 PyNumber_to_PyFloat(PyObject *pynum, const Options *options)
102 {
103 if (Options_Has_NaN_Sub(options) && PyNumber_IsNAN(pynum)) {
104 return Options_Return_NaN_Sub(options);
105 }
106 else if (Options_Has_INF_Sub(options) && PyNumber_IsINF(pynum)) {
107 return Options_Return_INF_Sub(options);
108 }
109 else {
110 return PyNumber_Float(pynum);
111 }
112 }
113
114
115 static PyObject *
PyNumber_to_PyInt(PyObject * pynum,const Options * options)116 PyNumber_to_PyInt(PyObject *pynum, const Options *options)
117 {
118 if (PyFloat_Check(pynum)) { /* Watch out for un-intable numbers. */
119 const double d = PyFloat_AS_DOUBLE(pynum);
120 if (Py_IS_INFINITY(d)) {
121 if (Options_Should_Raise(options))
122 PyErr_SetString(PyExc_OverflowError,
123 "cannot convert float infinity to integer");
124 return NULL;
125 }
126 if (Py_IS_NAN(d)) {
127 if (Options_Should_Raise(options))
128 PyErr_SetString(PyExc_ValueError,
129 "cannot convert float NaN to integer");
130 return NULL;
131 }
132 }
133 return PyNumber_Long(pynum);
134 }
135
136
137 PyObject *
PyFloat_to_PyInt(PyObject * fobj,const Options * options)138 PyFloat_to_PyInt(PyObject *fobj, const Options *options)
139 {
140 PyObject *tmp = PyNumber_to_PyInt(fobj, options);
141 Py_DECREF(fobj);
142 return tmp;
143 }
144
145
146 bool
PyFloat_is_Intlike(PyObject * obj)147 PyFloat_is_Intlike(PyObject *obj)
148 {
149 const double dval = PyFloat_AS_DOUBLE(obj);
150 if (!PyFloat_Check(obj)) {
151 return false;
152 }
153 if (dval < INT64_MAX && dval > INT64_MIN) {
154 return dval == (int64_t) dval;
155 }
156 return _PyFloat_is_Intlike(obj);
157 }
158
159
160 /* Convert a PyNumber to the desired PyNumber type. */
161 PyObject *
PyNumber_to_PyNumber(PyObject * pynum,const PyNumberType type,const Options * options)162 PyNumber_to_PyNumber(PyObject *pynum, const PyNumberType type,
163 const Options *options)
164 {
165 PyObject *pyresult = NULL;
166 switch (type) {
167 case REAL:
168 pyresult = PyNumber_to_PyInt_or_PyFloat(pynum, options);
169 break;
170 case FLOAT:
171 pyresult = PyNumber_to_PyFloat(pynum, options);
172 break;
173 case INT:
174 case FORCEINT:
175 case INTLIKE:
176 pyresult = PyNumber_to_PyInt(pynum, options);
177 break;
178 }
179 /* Clear any error if the result is NULL
180 * and we do not want to raise on errors.
181 */
182 if (pyresult == NULL && !Options_Should_Raise(options)) {
183 PyErr_Clear();
184 }
185 return pyresult;
186 }
187
188
189 /* Check that a PyNumber is the desired PyNumber type. */
190 bool
PyNumber_is_type(PyObject * obj,const PyNumberType type)191 PyNumber_is_type(PyObject *obj, const PyNumberType type)
192 {
193 register bool result = false;
194 switch (type) {
195 case REAL:
196 result = true;
197 break;
198 case FLOAT:
199 result = PyFloat_Check(obj);
200 break;
201 case INT:
202 result = PyLong_Check(obj);
203 break;
204 case INTLIKE:
205 case FORCEINT:
206 result = PyLong_Check(obj) || PyFloat_is_Intlike(obj);
207 break;
208 }
209 return result;
210 }
211