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