this repo has no description
at trunk 313 lines 9.3 kB view raw
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