return Py_BuildValue("(d)", ((PyFloatObject *)self)->ob_fval);
}
-/* this is for the benefit of the pack/unpack routines below */
-typedef enum _py_float_format_type float_format_type;
-#define unknown_format _py_float_format_unknown
-#define ieee_big_endian_format _py_float_format_ieee_big_endian
-#define ieee_little_endian_format _py_float_format_ieee_little_endian
-
-#define float_format (_PyRuntime.float_state.float_format)
-#define double_format (_PyRuntime.float_state.double_format)
-
/*[clinic input]
@permit_long_docstring_body
It exists mainly to be used in Python's test suite.
-This function returns whichever of 'unknown', 'IEEE, big-endian' or 'IEEE,
+This function returns whichever of 'IEEE, big-endian' or 'IEEE,
little-endian' best describes the format of floating-point numbers used by the
C type named by typestr.
[clinic start generated code]*/
static PyObject *
float___getformat___impl(PyTypeObject *type, const char *typestr)
-/*[clinic end generated code: output=2bfb987228cc9628 input=d2735823bfe8e81e]*/
+/*[clinic end generated code: output=2bfb987228cc9628 input=0ae1ba35d192f704]*/
{
- float_format_type r;
-
- if (strcmp(typestr, "double") == 0) {
- r = double_format;
- }
- else if (strcmp(typestr, "float") == 0) {
- r = float_format;
- }
- else {
+ if (strcmp(typestr, "double") != 0 && strcmp(typestr, "float") != 0) {
PyErr_SetString(PyExc_ValueError,
"__getformat__() argument 1 must be "
"'double' or 'float'");
return NULL;
}
-
- switch (r) {
- case unknown_format:
- return PyUnicode_FromString("unknown");
- case ieee_little_endian_format:
- return PyUnicode_FromString("IEEE, little-endian");
- case ieee_big_endian_format:
- return PyUnicode_FromString("IEEE, big-endian");
- default:
- PyErr_SetString(PyExc_RuntimeError,
- "insane float_format or double_format");
- return NULL;
- }
+ return PyUnicode_FromString(_PY_FLOAT_LITTLE_ENDIAN ?
+ "IEEE, little-endian" : "IEEE, big-endian");
}
-
static PyObject *
float_getreal(PyObject *v, void *Py_UNUSED(closure))
{
.tp_version_tag = _Py_TYPE_VERSION_FLOAT,
};
-static void
-_init_global_state(void)
-{
- float_format_type detected_double_format, detected_float_format;
-
- /* We attempt to determine if this machine is using IEEE
- floating-point formats by peering at the bits of some
- carefully chosen values. If it looks like we are on an
- IEEE platform, the float packing/unpacking routines can
- just copy bits, if not they resort to arithmetic & shifts
- and masks. The shifts & masks approach works on all finite
- values, but what happens to infinities, NaNs and signed
- zeroes on packing is an accident, and attempting to unpack
- a NaN or an infinity will raise an exception.
-
- Note that if we're on some whacked-out platform which uses
- IEEE formats but isn't strictly little-endian or big-
- endian, we will fall back to the portable shifts & masks
- method. */
-
-#if SIZEOF_DOUBLE == 8
- {
- double x = 9006104071832581.0;
- if (memcmp(&x, "\x43\x3f\xff\x01\x02\x03\x04\x05", 8) == 0)
- detected_double_format = ieee_big_endian_format;
- else if (memcmp(&x, "\x05\x04\x03\x02\x01\xff\x3f\x43", 8) == 0)
- detected_double_format = ieee_little_endian_format;
- else
- detected_double_format = unknown_format;
- }
-#else
- detected_double_format = unknown_format;
-#endif
-
-#if SIZEOF_FLOAT == 4
- {
- float y = 16711938.0;
- if (memcmp(&y, "\x4b\x7f\x01\x02", 4) == 0)
- detected_float_format = ieee_big_endian_format;
- else if (memcmp(&y, "\x02\x01\x7f\x4b", 4) == 0)
- detected_float_format = ieee_little_endian_format;
- else
- detected_float_format = unknown_format;
- }
-#else
- detected_float_format = unknown_format;
-#endif
-
- double_format = detected_double_format;
- float_format = detected_float_format;
-}
-
-void
-_PyFloat_InitState(PyInterpreterState *interp)
-{
- if (!_Py_IsMainInterpreter(interp)) {
- return;
- }
- _init_global_state();
-}
-
PyStatus
_PyFloat_InitTypes(PyInterpreterState *interp)
{
PyFloat_Pack4(double x, char *data, int le)
{
unsigned char *p = (unsigned char *)data;
- if (float_format == unknown_format) {
- unsigned char sign;
- int e;
- double f;
- unsigned int fbits;
- int incr = 1;
-
- if (le) {
- p += 3;
- incr = -1;
- }
-
- if (x < 0) {
- sign = 1;
- x = -x;
- }
- else
- sign = 0;
-
- f = frexp(x, &e);
-
- /* Normalize f to be in the range [1.0, 2.0) */
- if (0.5 <= f && f < 1.0) {
- f *= 2.0;
- e--;
- }
- else if (f == 0.0)
- e = 0;
- else {
- PyErr_SetString(PyExc_SystemError,
- "frexp() result out of range");
- return -1;
- }
-
- if (e >= 128)
- goto Overflow;
- else if (e < -126) {
- /* Gradual underflow */
- f = ldexp(f, 126 + e);
- e = 0;
- }
- else if (!(e == 0 && f == 0.0)) {
- e += 127;
- f -= 1.0; /* Get rid of leading 1 */
- }
-
- f *= 8388608.0; /* 2**23 */
- fbits = (unsigned int)(f + 0.5); /* Round */
- assert(fbits <= 8388608);
- if (fbits >> 23) {
- /* The carry propagated out of a string of 23 1 bits. */
- fbits = 0;
- ++e;
- if (e >= 255)
- goto Overflow;
- }
-
- /* First byte */
- *p = (sign << 7) | (e >> 1);
- p += incr;
-
- /* Second byte */
- *p = (char) (((e & 1) << 7) | (fbits >> 16));
- p += incr;
-
- /* Third byte */
- *p = (fbits >> 8) & 0xFF;
- p += incr;
-
- /* Fourth byte */
- *p = fbits & 0xFF;
-
- /* Done */
- return 0;
+ float y = (float)x;
+ int i, incr = 1;
+ if (isinf(y) && !isinf(x)) {
+ PyErr_SetString(PyExc_OverflowError,
+ "float too large to pack with f format");
+ return -1;
}
- else {
- float y = (float)x;
- int i, incr = 1;
-
- if (isinf(y) && !isinf(x))
- goto Overflow;
- /* correct y if x was a sNaN, transformed to qNaN by conversion */
- if (isnan(x)) {
- uint64_t v;
+ /* correct y if x was a sNaN, transformed to qNaN by conversion */
+ if (isnan(x)) {
+ uint64_t v;
- memcpy(&v, &x, 8);
+ memcpy(&v, &x, 8);
#ifndef __riscv
- if ((v & (1ULL << 51)) == 0) {
- uint32_t u32;
- memcpy(&u32, &y, 4);
- /* if have payload, make sNaN */
- if (u32 & 0x3fffff) {
- u32 &= ~(1 << 22);
- }
- memcpy(&y, &u32, 4);
- }
-#else
+ if ((v & (1ULL << 51)) == 0) {
uint32_t u32;
-
memcpy(&u32, &y, 4);
- /* Workaround RISC-V: "If a NaN value is converted to a
- * different floating-point type, the result is the
- * canonical NaN of the new type". The canonical NaN here
- * is a positive qNaN with zero payload. */
- if (v & (1ULL << 63)) {
- u32 |= (1 << 31); /* set sign */
- }
- /* add payload */
- u32 -= (u32 & 0x3fffff);
- u32 += (uint32_t)((v & 0x7ffffffffffffULL) >> 29);
/* if have payload, make sNaN */
- if ((v & (1ULL << 51)) == 0 && (u32 & 0x3fffff)) {
+ if (u32 & 0x3fffff) {
u32 &= ~(1 << 22);
}
-
memcpy(&y, &u32, 4);
-#endif
}
+#else
+ uint32_t u32;
+
+ memcpy(&u32, &y, 4);
+ /* Workaround RISC-V: "If a NaN value is converted to a
+ * different floating-point type, the result is the
+ * canonical NaN of the new type". The canonical NaN here
+ * is a positive qNaN with zero payload. */
+ if (v & (1ULL << 63)) {
+ u32 |= (1 << 31); /* set sign */
+ }
+ /* add payload */
+ u32 -= (u32 & 0x3fffff);
+ u32 += (uint32_t)((v & 0x7ffffffffffffULL) >> 29);
+ /* if have payload, make sNaN */
+ if ((v & (1ULL << 51)) == 0 && (u32 & 0x3fffff)) {
+ u32 &= ~(1 << 22);
+ }
+
+ memcpy(&y, &u32, 4);
+#endif
+ }
- unsigned char s[sizeof(float)];
- memcpy(s, &y, sizeof(float));
+ unsigned char s[sizeof(float)];
+ memcpy(s, &y, sizeof(float));
- if ((float_format == ieee_little_endian_format && !le)
- || (float_format == ieee_big_endian_format && le)) {
- p += 3;
- incr = -1;
- }
+ if ((_PY_FLOAT_LITTLE_ENDIAN && !le) || (_PY_FLOAT_BIG_ENDIAN && le)) {
+ p += 3;
+ incr = -1;
+ }
- for (i = 0; i < 4; i++) {
- *p = s[i];
- p += incr;
- }
- return 0;
+ for (i = 0; i < 4; i++) {
+ *p = s[i];
+ p += incr;
}
- Overflow:
- PyErr_SetString(PyExc_OverflowError,
- "float too large to pack with f format");
- return -1;
+ return 0;
}
int
PyFloat_Pack8(double x, char *data, int le)
{
unsigned char *p = (unsigned char *)data;
- if (double_format == unknown_format) {
- unsigned char sign;
- int e;
- double f;
- unsigned int fhi, flo;
- int incr = 1;
-
- if (le) {
- p += 7;
- incr = -1;
- }
-
- if (x < 0) {
- sign = 1;
- x = -x;
- }
- else
- sign = 0;
-
- f = frexp(x, &e);
-
- /* Normalize f to be in the range [1.0, 2.0) */
- if (0.5 <= f && f < 1.0) {
- f *= 2.0;
- e--;
- }
- else if (f == 0.0)
- e = 0;
- else {
- PyErr_SetString(PyExc_SystemError,
- "frexp() result out of range");
- return -1;
- }
+ unsigned char as_bytes[8];
+ memcpy(as_bytes, &x, 8);
+ const unsigned char *s = as_bytes;
+ int i, incr = 1;
- if (e >= 1024)
- goto Overflow;
- else if (e < -1022) {
- /* Gradual underflow */
- f = ldexp(f, 1022 + e);
- e = 0;
- }
- else if (!(e == 0 && f == 0.0)) {
- e += 1023;
- f -= 1.0; /* Get rid of leading 1 */
- }
-
- /* fhi receives the high 28 bits; flo the low 24 bits (== 52 bits) */
- f *= 268435456.0; /* 2**28 */
- fhi = (unsigned int)f; /* Truncate */
- assert(fhi < 268435456);
-
- f -= (double)fhi;
- f *= 16777216.0; /* 2**24 */
- flo = (unsigned int)(f + 0.5); /* Round */
- assert(flo <= 16777216);
- if (flo >> 24) {
- /* The carry propagated out of a string of 24 1 bits. */
- flo = 0;
- ++fhi;
- if (fhi >> 28) {
- /* And it also propagated out of the next 28 bits. */
- fhi = 0;
- ++e;
- if (e >= 2047)
- goto Overflow;
- }
- }
-
- /* First byte */
- *p = (sign << 7) | (e >> 4);
- p += incr;
-
- /* Second byte */
- *p = (unsigned char) (((e & 0xF) << 4) | (fhi >> 24));
- p += incr;
-
- /* Third byte */
- *p = (fhi >> 16) & 0xFF;
- p += incr;
-
- /* Fourth byte */
- *p = (fhi >> 8) & 0xFF;
- p += incr;
-
- /* Fifth byte */
- *p = fhi & 0xFF;
- p += incr;
-
- /* Sixth byte */
- *p = (flo >> 16) & 0xFF;
- p += incr;
-
- /* Seventh byte */
- *p = (flo >> 8) & 0xFF;
- p += incr;
-
- /* Eighth byte */
- *p = flo & 0xFF;
- /* p += incr; */
-
- /* Done */
- return 0;
-
- Overflow:
- PyErr_SetString(PyExc_OverflowError,
- "float too large to pack with d format");
- return -1;
+ if ((_PY_FLOAT_LITTLE_ENDIAN && !le) || (_PY_FLOAT_BIG_ENDIAN && le)) {
+ p += 7;
+ incr = -1;
}
- else {
- unsigned char as_bytes[8];
- memcpy(as_bytes, &x, 8);
- const unsigned char *s = as_bytes;
- int i, incr = 1;
-
- if ((double_format == ieee_little_endian_format && !le)
- || (double_format == ieee_big_endian_format && le)) {
- p += 7;
- incr = -1;
- }
- for (i = 0; i < 8; i++) {
- *p = *s++;
- p += incr;
- }
- return 0;
+ for (i = 0; i < 8; i++) {
+ *p = *s++;
+ p += incr;
}
+ return 0;
}
double
PyFloat_Unpack4(const char *data, int le)
{
unsigned char *p = (unsigned char *)data;
- if (float_format == unknown_format) {
- unsigned char sign;
- int e;
- unsigned int f;
- double x;
- int incr = 1;
-
- if (le) {
- p += 3;
- incr = -1;
- }
+ float x;
- /* First byte */
- sign = (*p >> 7) & 1;
- e = (*p & 0x7F) << 1;
- p += incr;
+ if ((_PY_FLOAT_LITTLE_ENDIAN && !le) || (_PY_FLOAT_BIG_ENDIAN && le)) {
+ char buf[4];
+ char *d = &buf[3];
+ int i;
- /* Second byte */
- e |= (*p >> 7) & 1;
- f = (*p & 0x7F) << 16;
- p += incr;
-
- if (e == 255) {
- PyErr_SetString(
- PyExc_ValueError,
- "can't unpack IEEE 754 special value "
- "on non-IEEE platform");
- return -1;
- }
-
- /* Third byte */
- f |= *p << 8;
- p += incr;
-
- /* Fourth byte */
- f |= *p;
-
- x = (double)f / 8388608.0;
-
- /* XXX This sadly ignores Inf/NaN issues */
- if (e == 0)
- e = -126;
- else {
- x += 1.0;
- e -= 127;
+ for (i = 0; i < 4; i++) {
+ *d-- = *p++;
}
- x = ldexp(x, e);
-
- if (sign)
- x = -x;
-
- return x;
+ memcpy(&x, buf, 4);
}
else {
- float x;
-
- if ((float_format == ieee_little_endian_format && !le)
- || (float_format == ieee_big_endian_format && le)) {
- char buf[4];
- char *d = &buf[3];
- int i;
-
- for (i = 0; i < 4; i++) {
- *d-- = *p++;
- }
- memcpy(&x, buf, 4);
- }
- else {
- memcpy(&x, p, 4);
- }
+ memcpy(&x, p, 4);
+ }
- /* return sNaN double if x was sNaN float */
- if (isnan(x)) {
- uint32_t v;
- memcpy(&v, &x, 4);
+ /* return sNaN double if x was sNaN float */
+ if (isnan(x)) {
+ uint32_t v;
+ memcpy(&v, &x, 4);
#ifndef __riscv
- if ((v & (1 << 22)) == 0) {
- double y = x; /* will make qNaN double */
- uint64_t u64;
- memcpy(&u64, &y, 8);
- u64 &= ~(1ULL << 51); /* make sNaN */
- memcpy(&y, &u64, 8);
- return y;
- }
-#else
- double y = x;
+ if ((v & (1 << 22)) == 0) {
+ double y = x; /* will make qNaN double */
uint64_t u64;
-
memcpy(&u64, &y, 8);
- if ((v & (1 << 22)) == 0) {
- u64 &= ~(1ULL << 51);
- }
- /* Workaround RISC-V, see PyFloat_Pack4() */
- if (v & (1 << 31)) {
- u64 |= (1ULL << 63); /* set sign */
- }
- /* add payload */
- u64 -= (u64 & 0x7ffffffffffffULL);
- u64 += ((v & 0x3fffffULL) << 29);
-
+ u64 &= ~(1ULL << 51); /* make sNaN */
memcpy(&y, &u64, 8);
return y;
-#endif
}
+#else
+ double y = x;
+ uint64_t u64;
- return x;
+ memcpy(&u64, &y, 8);
+ if ((v & (1 << 22)) == 0) {
+ u64 &= ~(1ULL << 51);
+ }
+ /* Workaround RISC-V, see PyFloat_Pack4() */
+ if (v & (1 << 31)) {
+ u64 |= (1ULL << 63); /* set sign */
+ }
+ /* add payload */
+ u64 -= (u64 & 0x7ffffffffffffULL);
+ u64 += ((v & 0x3fffffULL) << 29);
+
+ memcpy(&y, &u64, 8);
+ return y;
+#endif
}
+
+ return x;
}
double
PyFloat_Unpack8(const char *data, int le)
{
unsigned char *p = (unsigned char *)data;
- if (double_format == unknown_format) {
- unsigned char sign;
- int e;
- unsigned int fhi, flo;
- double x;
- int incr = 1;
-
- if (le) {
- p += 7;
- incr = -1;
- }
-
- /* First byte */
- sign = (*p >> 7) & 1;
- e = (*p & 0x7F) << 4;
-
- p += incr;
-
- /* Second byte */
- e |= (*p >> 4) & 0xF;
- fhi = (*p & 0xF) << 24;
- p += incr;
-
- if (e == 2047) {
- PyErr_SetString(
- PyExc_ValueError,
- "can't unpack IEEE 754 special value "
- "on non-IEEE platform");
- return -1.0;
- }
-
- /* Third byte */
- fhi |= *p << 16;
- p += incr;
-
- /* Fourth byte */
- fhi |= *p << 8;
- p += incr;
-
- /* Fifth byte */
- fhi |= *p;
- p += incr;
-
- /* Sixth byte */
- flo = *p << 16;
- p += incr;
-
- /* Seventh byte */
- flo |= *p << 8;
- p += incr;
-
- /* Eighth byte */
- flo |= *p;
+ double x;
- x = (double)fhi + (double)flo / 16777216.0; /* 2**24 */
- x /= 268435456.0; /* 2**28 */
+ if ((_PY_FLOAT_LITTLE_ENDIAN && !le) || (_PY_FLOAT_BIG_ENDIAN && le)) {
+ char buf[8];
+ char *d = &buf[7];
+ int i;
- if (e == 0)
- e = -1022;
- else {
- x += 1.0;
- e -= 1023;
+ for (i = 0; i < 8; i++) {
+ *d-- = *p++;
}
- x = ldexp(x, e);
-
- if (sign)
- x = -x;
-
- return x;
+ memcpy(&x, buf, 8);
}
else {
- double x;
-
- if ((double_format == ieee_little_endian_format && !le)
- || (double_format == ieee_big_endian_format && le)) {
- char buf[8];
- char *d = &buf[7];
- int i;
-
- for (i = 0; i < 8; i++) {
- *d-- = *p++;
- }
- memcpy(&x, buf, 8);
- }
- else {
- memcpy(&x, p, 8);
- }
-
- return x;
+ memcpy(&x, p, 8);
}
+
+ return x;
}