this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2// floatobject.c implementation
3
4#include <cfloat>
5#include <cmath>
6
7#include "cpython-data.h"
8#include "cpython-func.h"
9
10#include "api-handle.h"
11#include "bytearrayobject-utils.h"
12#include "bytesobject-utils.h"
13#include "float-builtins.h"
14#include "objects.h"
15#include "runtime.h"
16#include "typeslots.h"
17
18namespace py {
19PY_EXPORT PyObject* PyFloat_FromDouble(double fval) {
20 Runtime* runtime = Thread::current()->runtime();
21 return ApiHandle::newReferenceWithManaged(runtime, runtime->newFloat(fval));
22}
23
24PY_EXPORT double PyFloat_AsDouble(PyObject* op) {
25 Thread* thread = Thread::current();
26 if (op == nullptr) {
27 thread->raiseBadArgument();
28 return -1;
29 }
30
31 // Object is float
32 HandleScope scope(thread);
33 Object obj(&scope, ApiHandle::asObject(ApiHandle::fromPyObject(op)));
34 if (!thread->runtime()->isInstanceOfFloat(*obj)) {
35 obj = thread->invokeFunction1(ID(builtins), ID(_float), obj);
36 if (obj.isError()) return -1;
37 }
38 return floatUnderlying(*obj).value();
39}
40
41PY_EXPORT int PyFloat_CheckExact_Func(PyObject* obj) {
42 return ApiHandle::asObject(ApiHandle::fromPyObject(obj)).isFloat();
43}
44
45PY_EXPORT int PyFloat_Check_Func(PyObject* obj) {
46 return Thread::current()->runtime()->isInstanceOfFloat(
47 ApiHandle::asObject(ApiHandle::fromPyObject(obj)));
48}
49
50PY_EXPORT PyObject* PyFloat_FromString(PyObject* obj) {
51 Thread* thread = Thread::current();
52
53 DCHECK(obj != nullptr,
54 "null argument to internal routine PyFloat_FromString");
55
56 HandleScope scope(thread);
57 Runtime* runtime = thread->runtime();
58 ApiHandle* handle = ApiHandle::fromPyObject(obj);
59 Object object(&scope, ApiHandle::asObject(handle));
60
61 // First, handle all string like items here
62 if (runtime->isInstanceOfStr(*object) ||
63 runtime->isInstanceOfBytes(*object) ||
64 runtime->isInstanceOfBytearray(*object)) {
65 object = thread->invokeFunction1(ID(builtins), ID(float), object);
66 return object.isError()
67 ? nullptr
68 : ApiHandle::newReferenceWithManaged(runtime, *object);
69 }
70
71 if (object.isMemoryView()) {
72 // Memoryviews are buffer like, but can be converted to bytes and then to a
73 // float
74 MemoryView memoryview(&scope, *object);
75 Object buffer(&scope, memoryview.buffer());
76 // Buffer is either a bytes object or a raw pointer
77 if (runtime->isInstanceOfBytes(*buffer)) {
78 Bytes bytes(&scope, bytesUnderlying(*object));
79 object = thread->invokeFunction1(ID(builtins), ID(float), bytes);
80 return object.isError()
81 ? nullptr
82 : ApiHandle::newReferenceWithManaged(runtime, *object);
83 }
84 Pointer underlying_pointer(&scope, *buffer);
85 word length = memoryview.length();
86 unique_c_ptr<char> copy(::strndup(
87 reinterpret_cast<const char*>(underlying_pointer.cptr()), length));
88
89 object = floatFromDigits(thread, copy.get(), length);
90 return object.isError()
91 ? nullptr
92 : ApiHandle::newReferenceWithManaged(runtime, *object);
93 }
94 // Maybe it otherwise supports the buffer protocol
95 Object bytes(&scope, newBytesFromBuffer(thread, object));
96 if (!bytes.isError()) {
97 object = thread->invokeFunction1(ID(builtins), ID(float), bytes);
98 return object.isError()
99 ? nullptr
100 : ApiHandle::newReferenceWithManaged(runtime, *object);
101 }
102
103 thread->clearPendingException();
104 thread->raiseWithFmt(
105 LayoutId::kTypeError,
106 "float() argument must be a string or a number, not '%T'", &object);
107 return nullptr;
108}
109
110PY_EXPORT PyObject* PyFloat_GetInfo() { UNIMPLEMENTED("PyFloat_GetInfo"); }
111
112PY_EXPORT double PyFloat_GetMax() { return DBL_MAX; }
113
114PY_EXPORT double PyFloat_GetMin() { return DBL_MIN; }
115
116PY_EXPORT PyTypeObject* PyFloat_Type_Ptr() {
117 Runtime* runtime = Thread::current()->runtime();
118 return reinterpret_cast<PyTypeObject*>(
119 ApiHandle::borrowedReference(runtime, runtime->typeAt(LayoutId::kFloat)));
120}
121
122// _PyFloat_{Pack,Unpack}{2,4,8}. See floatobject.h.
123// To match the NPY_HALF_ROUND_TIES_TO_EVEN behavior in:
124// https://github.com/numpy/numpy/blob/master/numpy/core/src/npymath/halffloat.c
125// We use:
126// bits = (unsigned short)f; Note the truncation
127// if ((f - bits > 0.5) || (f - bits == 0.5 && bits % 2)) {
128// bits++;
129// }
130PY_EXPORT int _PyFloat_Pack2(double x, unsigned char* p, int little_endian) {
131 int e;
132 unsigned short bits;
133 bool sign = std::signbit(x);
134 if (x == 0.0) {
135 e = 0;
136 bits = 0;
137 } else if (!std::isfinite(x)) {
138 e = 0x1f;
139 if (std::isinf(x)) {
140 bits = 0;
141 } else {
142 DCHECK(std::isnan(x), "remaining case must be NaN");
143 // There are 2046 distinct half-precision NaNs (1022 signaling and
144 // 1024 quiet), but there are only two quiet NaNs that don't arise by
145 // quieting a signaling NaN; we get those by setting the topmost bit
146 // of the fraction field and clearing all other fraction bits. We
147 // choose the one with the appropriate sign.
148 bits = 512;
149 }
150 } else {
151 if (sign) {
152 x = -x;
153 }
154
155 double f = std::frexp(x, &e);
156 if (f < 0.5 || f >= 1.0) {
157 Thread::current()->raiseWithFmt(LayoutId::kSystemError,
158 "std::frexp() result out of range");
159 return -1;
160 }
161
162 // Normalize f to be in the range [1.0, 2.0)
163 f *= 2.0;
164 e--;
165
166 if (e >= 16) {
167 Thread::current()->raiseWithFmt(LayoutId::kOverflowError,
168 "float too large to pack with e format");
169 return -1;
170 }
171
172 if (e < -25) {
173 // |x| < 2**-25. Underflow to zero.
174 f = 0.0;
175 e = 0;
176 } else if (e < -14) {
177 // |x| < 2**-14. Gradual underflow
178 f = std::ldexp(f, 14 + e);
179 e = 0;
180 } else { // if (!(e == 0 && f == 0.0))
181 e += 15;
182 f -= 1.0; // Get rid of leading 1
183 }
184
185 f *= 1024.0; // 2**10
186 // Round to even
187 bits = static_cast<unsigned short>(f); // Note the truncation
188 CHECK(bits < 1024, "Expected bits < 1024");
189 CHECK(e < 31, "Expected e < 31");
190 if ((f - bits > 0.5) || ((f - bits == 0.5) && (bits % 2 == 1))) {
191 ++bits;
192 if (bits == 1024) {
193 // The carry propagated out of a string of 10 1 bits.
194 bits = 0;
195 ++e;
196 if (e == 31) {
197 Thread::current()->raiseWithFmt(
198 LayoutId::kOverflowError,
199 "float too large to pack with e format");
200 return -1;
201 }
202 }
203 }
204 }
205
206 bits |= (e << 10) | (sign << 15);
207
208 // Write out result.
209 int incr = 1;
210 if (little_endian == (endian::native == endian::little)) {
211 p += 1;
212 incr = -1;
213 }
214
215 // First byte
216 *p = static_cast<unsigned char>((bits >> 8) & 0xFF);
217 p += incr;
218
219 /* Second byte */
220 *p = static_cast<unsigned char>(bits & 0xFF);
221 return 0;
222}
223
224PY_EXPORT int _PyFloat_Pack4(double x, unsigned char* p, int little_endian) {
225 // Assumes float format is ieee_little_endian_format
226 float y = static_cast<float>(x);
227 if (std::isinf(y) && !std::isinf(x)) {
228 Thread::current()->raiseWithFmt(LayoutId::kOverflowError,
229 "float too large to pack with f format");
230 return -1;
231 }
232
233 if (little_endian == (endian::native == endian::little)) {
234 std::memcpy(p, &y, sizeof(y));
235 } else {
236 int32_t as_int = bit_cast<uint32_t>(y);
237 int32_t swapped = __builtin_bswap32(as_int);
238 std::memcpy(p, &swapped, sizeof(swapped));
239 }
240
241 return 0;
242}
243
244PY_EXPORT int _PyFloat_Pack8(double x, unsigned char* p, int little_endian) {
245 // Assumes double format is ieee_little_endian_format
246 if (little_endian == (endian::native == endian::little)) {
247 std::memcpy(p, &x, sizeof(x));
248 } else {
249 int64_t as_int = bit_cast<int64_t>(x);
250 int64_t swapped = __builtin_bswap64(as_int);
251 std::memcpy(p, &swapped, sizeof(swapped));
252 }
253
254 return 0;
255}
256
257PY_EXPORT double _PyFloat_Unpack2(const unsigned char* p, int little_endian) {
258 int incr = 1;
259 if (little_endian == (endian::native == endian::little)) {
260 p += 1;
261 incr = -1;
262 }
263
264 // First byte
265 bool sign = (*p >> 7) & 1;
266 int e = (*p & 0x7C) >> 2;
267 unsigned int f = (*p & 0x03) << 8;
268 p += incr;
269
270 // Second byte
271 f |= *p;
272
273 if (e == 0x1f) {
274 // Infinity
275 if (f == 0) {
276 return _Py_dg_infinity(sign);
277 }
278 // NaN
279 return _Py_dg_stdnan(sign);
280 }
281
282 double x = static_cast<double>(f) / 1024.0;
283
284 if (e == 0) {
285 e = -14;
286 } else {
287 x += 1.0;
288 e -= 15;
289 }
290 x = std::ldexp(x, e);
291
292 return sign ? -x : x;
293}
294
295PY_EXPORT double _PyFloat_Unpack4(const unsigned char* p, int little_endian) {
296 // Assumes float format is ieee_little_endian_format
297 int32_t as_int = *reinterpret_cast<const int32_t*>(p);
298 if (little_endian != (endian::native == endian::little)) {
299 as_int = __builtin_bswap32(as_int);
300 }
301 return bit_cast<float>(as_int);
302}
303
304PY_EXPORT double _PyFloat_Unpack8(const unsigned char* p, int little_endian) {
305 // Assumes double format is ieee_little_endian_format
306 int64_t as_int = *reinterpret_cast<const int64_t*>(p);
307 if (little_endian != (endian::native == endian::little)) {
308 as_int = __builtin_bswap64(as_int);
309 }
310 return bit_cast<double>(as_int);
311}
312
313} // namespace py