summaryrefslogtreecommitdiffstats
path: root/Objects/floatobject.c
diff options
context:
space:
mode:
Diffstat (limited to 'Objects/floatobject.c')
-rw-r--r--Objects/floatobject.c748
1 files changed, 509 insertions, 239 deletions
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
index 55f43cb..c95b5c9 100644
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -983,8 +983,139 @@ float_getnewargs(PyFloatObject *v)
return Py_BuildValue("(d)", v->ob_fval);
}
+/* this is for the benefit of the pack/unpack routines below */
+
+typedef enum {
+ unknown_format, ieee_big_endian_format, ieee_little_endian_format
+} float_format_type;
+
+static float_format_type double_format, float_format;
+static float_format_type detected_double_format, detected_float_format;
+
+static PyObject *
+float_getformat(PyTypeObject *v, PyObject* arg)
+{
+ char* s;
+ float_format_type r;
+
+ if (!PyString_Check(arg)) {
+ PyErr_Format(PyExc_TypeError,
+ "__getformat__() argument must be string, not %.500s",
+ arg->ob_type->tp_name);
+ return NULL;
+ }
+ s = PyString_AS_STRING(arg);
+ if (strcmp(s, "double") == 0) {
+ r = double_format;
+ }
+ else if (strcmp(s, "float") == 0) {
+ r = float_format;
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError,
+ "__getformat__() argument 1 must be "
+ "'double' or 'float'");
+ return NULL;
+ }
+
+ switch (r) {
+ case unknown_format:
+ return PyString_FromString("unknown");
+ case ieee_little_endian_format:
+ return PyString_FromString("IEEE, little-endian");
+ case ieee_big_endian_format:
+ return PyString_FromString("IEEE, big-endian");
+ default:
+ Py_FatalError("insane float_format or double_format");
+ return NULL;
+ }
+}
+
+PyDoc_STRVAR(float_getformat_doc,
+"float.__getformat__(typestr) -> string\n"
+"\n"
+"You probably don't want to use this function. It exists mainly to be\n"
+"used in Python's test suite.\n"
+"\n"
+"typestr must be 'double' or 'float'. This function returns whichever of\n"
+"'unknown', 'IEEE, big-endian' or 'IEEE, little-endian' best describes the\n"
+"format of floating point numbers used by the C type named by typestr.");
+
+static PyObject *
+float_setformat(PyTypeObject *v, PyObject* args)
+{
+ char* typestr;
+ char* format;
+ float_format_type f;
+ float_format_type detected;
+ float_format_type *p;
+
+ if (!PyArg_ParseTuple(args, "ss:__setformat__", &typestr, &format))
+ return NULL;
+
+ if (strcmp(typestr, "double") == 0) {
+ p = &double_format;
+ detected = detected_double_format;
+ }
+ else if (strcmp(typestr, "float") == 0) {
+ p = &float_format;
+ detected = detected_float_format;
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError,
+ "__setformat__() argument 1 must "
+ "be 'double' or 'float'");
+ return NULL;
+ }
+
+ if (strcmp(format, "unknown") == 0) {
+ f = unknown_format;
+ }
+ else if (strcmp(format, "IEEE, little-endian") == 0) {
+ f = ieee_little_endian_format;
+ }
+ else if (strcmp(format, "IEEE, big-endian") == 0) {
+ f = ieee_big_endian_format;
+ }
+ else {
+ PyErr_SetString(PyExc_ValueError,
+ "__setformat__() argument 2 must be "
+ "'unknown', 'IEEE, little-endian' or "
+ "'IEEE, big-endian'");
+ return NULL;
+
+ }
+
+ if (f != unknown_format && f != detected) {
+ PyErr_Format(PyExc_ValueError,
+ "can only set %s format to 'unknown' or the "
+ "detected platform value", typestr);
+ return NULL;
+ }
+
+ *p = f;
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(float_setformat_doc,
+"float.__setformat__(typestr, fmt) -> None\n"
+"\n"
+"You probably don't want to use this function. It exists mainly to be\n"
+"used in Python's test suite.\n"
+"\n"
+"typestr must be 'double' or 'float'. fmt must be one of 'unknown',\n"
+"'IEEE, big-endian' or 'IEEE, little-endian', and in addition can only be\n"
+"one of the latter two if it appears to match the underlying C reality.\n"
+"\n"
+"Overrides the automatic determination of C-level floating point type.\n"
+"This affects how floats are converted to and from binary strings.");
+
static PyMethodDef float_methods[] = {
{"__getnewargs__", (PyCFunction)float_getnewargs, METH_NOARGS},
+ {"__getformat__", (PyCFunction)float_getformat,
+ METH_O|METH_CLASS, float_getformat_doc},
+ {"__setformat__", (PyCFunction)float_setformat,
+ METH_VARARGS|METH_CLASS, float_setformat_doc},
{NULL, NULL} /* sentinel */
};
@@ -1079,6 +1210,56 @@ PyTypeObject PyFloat_Type = {
};
void
+_PyFloat_Init(void)
+{
+ /* 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_Fini(void)
{
PyFloatObject *p;
@@ -1165,306 +1346,395 @@ PyFloat_Fini(void)
int
_PyFloat_Pack4(double x, unsigned char *p, int le)
{
- 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;
+ if (float_format == unknown_format) {
+ unsigned char sign;
+ int e;
+ double f;
+ unsigned int fbits;
+ int incr = 1;
+
+ if (le) {
+ p += 3;
+ incr = -1;
+ }
- f = frexp(x, &e);
+ if (x < 0) {
+ sign = 1;
+ x = -x;
+ }
+ else
+ sign = 0;
- /* 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;
- }
+ f = frexp(x, &e);
- 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 */
- }
+ /* 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;
+ }
- 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)
+ 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;
+ /* First byte */
+ *p = (sign << 7) | (e >> 1);
+ p += incr;
- /* Second byte */
- *p = (char) (((e & 1) << 7) | (fbits >> 16));
- p += incr;
+ /* Second byte */
+ *p = (char) (((e & 1) << 7) | (fbits >> 16));
+ p += incr;
- /* Third byte */
- *p = (fbits >> 8) & 0xFF;
- p += incr;
+ /* Third byte */
+ *p = (fbits >> 8) & 0xFF;
+ p += incr;
- /* Fourth byte */
- *p = fbits & 0xFF;
+ /* Fourth byte */
+ *p = fbits & 0xFF;
- /* Done */
- return 0;
+ /* Done */
+ return 0;
- Overflow:
- PyErr_SetString(PyExc_OverflowError,
- "float too large to pack with f format");
- return -1;
+ Overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "float too large to pack with f format");
+ return -1;
+ }
+ else {
+ float y = x;
+ const char *s = (char*)&y;
+ int i, incr = 1;
+
+ if ((float_format == ieee_little_endian_format && !le)
+ || (float_format == ieee_big_endian_format && le)) {
+ p += 3;
+ incr = -1;
+ }
+
+ for (i = 0; i < 4; i++) {
+ *p = *s++;
+ p += incr;
+ }
+ return 0;
+ }
}
int
_PyFloat_Pack8(double x, unsigned char *p, int le)
{
- unsigned char sign;
- int e;
- double f;
- unsigned int fhi, flo;
- int incr = 1;
+ 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 (le) {
- p += 7;
- incr = -1;
- }
+ if (x < 0) {
+ sign = 1;
+ x = -x;
+ }
+ else
+ sign = 0;
- if (x < 0) {
- sign = 1;
- x = -x;
- }
- else
- sign = 0;
+ f = frexp(x, &e);
- 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;
+ }
- /* 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 >= 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 */
+ }
- 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 progagated out of the next 28 bits. */
- fhi = 0;
- ++e;
- if (e >= 2047)
- goto Overflow;
+ /* 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 progagated out of the next 28 bits. */
+ fhi = 0;
+ ++e;
+ if (e >= 2047)
+ goto Overflow;
+ }
}
- }
- /* First byte */
- *p = (sign << 7) | (e >> 4);
- p += incr;
+ /* First byte */
+ *p = (sign << 7) | (e >> 4);
+ p += incr;
- /* Second byte */
- *p = (unsigned char) (((e & 0xF) << 4) | (fhi >> 24));
- p += incr;
+ /* Second byte */
+ *p = (unsigned char) (((e & 0xF) << 4) | (fhi >> 24));
+ p += incr;
- /* Third byte */
- *p = (fhi >> 16) & 0xFF;
- p += incr;
+ /* Third byte */
+ *p = (fhi >> 16) & 0xFF;
+ p += incr;
- /* Fourth byte */
- *p = (fhi >> 8) & 0xFF;
- p += incr;
+ /* Fourth byte */
+ *p = (fhi >> 8) & 0xFF;
+ p += incr;
- /* Fifth byte */
- *p = fhi & 0xFF;
- p += incr;
+ /* Fifth byte */
+ *p = fhi & 0xFF;
+ p += incr;
- /* Sixth byte */
- *p = (flo >> 16) & 0xFF;
- p += incr;
+ /* Sixth byte */
+ *p = (flo >> 16) & 0xFF;
+ p += incr;
- /* Seventh byte */
- *p = (flo >> 8) & 0xFF;
- p += incr;
+ /* Seventh byte */
+ *p = (flo >> 8) & 0xFF;
+ p += incr;
- /* Eighth byte */
- *p = flo & 0xFF;
- p += incr;
+ /* Eighth byte */
+ *p = flo & 0xFF;
+ p += incr;
- /* Done */
- return 0;
+ /* Done */
+ return 0;
+
+ Overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "float too large to pack with d format");
+ return -1;
+ }
+ else {
+ const char *s = (char*)&x;
+ int i, incr = 1;
- Overflow:
- PyErr_SetString(PyExc_OverflowError,
- "float too large to pack with d format");
- return -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;
+ }
}
double
_PyFloat_Unpack4(const unsigned char *p, int le)
{
- unsigned char sign;
- int e;
- unsigned int f;
- double x;
- int incr = 1;
+ if (float_format == unknown_format) {
+ unsigned char sign;
+ int e;
+ unsigned int f;
+ double x;
+ int incr = 1;
+
+ if (le) {
+ p += 3;
+ incr = -1;
+ }
- if (le) {
- p += 3;
- incr = -1;
- }
+ /* First byte */
+ sign = (*p >> 7) & 1;
+ e = (*p & 0x7F) << 1;
+ p += incr;
+
+ /* 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;
+ }
- /* First byte */
- sign = (*p >> 7) & 1;
- e = (*p & 0x7F) << 1;
- p += incr;
+ /* Third byte */
+ f |= *p << 8;
+ p += incr;
- /* Second byte */
- e |= (*p >> 7) & 1;
- f = (*p & 0x7F) << 16;
- p += incr;
+ /* Fourth byte */
+ f |= *p;
- /* Third byte */
- f |= *p << 8;
- p += incr;
+ x = (double)f / 8388608.0;
- /* Fourth byte */
- f |= *p;
+ /* XXX This sadly ignores Inf/NaN issues */
+ if (e == 0)
+ e = -126;
+ else {
+ x += 1.0;
+ e -= 127;
+ }
+ x = ldexp(x, e);
- x = (double)f / 8388608.0;
+ if (sign)
+ x = -x;
- /* XXX This sadly ignores Inf/NaN issues */
- if (e == 0)
- e = -126;
- else {
- x += 1.0;
- e -= 127;
+ return x;
}
- x = ldexp(x, e);
-
- if (sign)
- x = -x;
-
- return x;
+ else {
+ if ((float_format == ieee_little_endian_format && !le)
+ || (float_format == ieee_big_endian_format && le)) {
+ char buf[8];
+ char *d = &buf[3];
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ *d-- = *p++;
+ }
+ return *(float*)&buf[0];
+ }
+ else {
+ return *(float*)p;
+ }
+ }
}
double
_PyFloat_Unpack8(const unsigned char *p, int le)
{
- unsigned char sign;
- int e;
- unsigned int fhi, flo;
- double x;
- int incr = 1;
+ 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;
+ }
- 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;
+ }
- /* First byte */
- sign = (*p >> 7) & 1;
- e = (*p & 0x7F) << 4;
- p += incr;
+ /* Third byte */
+ fhi |= *p << 16;
+ p += incr;
- /* Second byte */
- e |= (*p >> 4) & 0xF;
- fhi = (*p & 0xF) << 24;
- p += incr;
+ /* Fourth byte */
+ fhi |= *p << 8;
+ p += incr;
- /* Third byte */
- fhi |= *p << 16;
- p += incr;
+ /* Fifth byte */
+ fhi |= *p;
+ p += incr;
- /* Fourth byte */
- fhi |= *p << 8;
- p += incr;
+ /* Sixth byte */
+ flo = *p << 16;
+ p += incr;
- /* Fifth byte */
- fhi |= *p;
- p += incr;
+ /* Seventh byte */
+ flo |= *p << 8;
+ p += incr;
- /* Sixth byte */
- flo = *p << 16;
- p += incr;
+ /* Eighth byte */
+ flo |= *p;
- /* Seventh byte */
- flo |= *p << 8;
- p += incr;
+ x = (double)fhi + (double)flo / 16777216.0; /* 2**24 */
+ x /= 268435456.0; /* 2**28 */
- /* Eighth byte */
- flo |= *p;
+ if (e == 0)
+ e = -1022;
+ else {
+ x += 1.0;
+ e -= 1023;
+ }
+ x = ldexp(x, e);
- x = (double)fhi + (double)flo / 16777216.0; /* 2**24 */
- x /= 268435456.0; /* 2**28 */
+ if (sign)
+ x = -x;
- /* XXX This sadly ignores Inf/NaN */
- if (e == 0)
- e = -1022;
+ return x;
+ }
else {
- x += 1.0;
- e -= 1023;
+ 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++;
+ }
+ return *(double*)&buf[0];
+ }
+ else {
+ return *(double*)p;
+ }
}
- x = ldexp(x, e);
-
- if (sign)
- x = -x;
-
- return x;
}