summaryrefslogtreecommitdiffstats
path: root/Modules/structmodule.c
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>1997-01-02 22:21:36 (GMT)
committerGuido van Rossum <guido@python.org>1997-01-02 22:21:36 (GMT)
commit74679b455fad59a38f4172ae883244c1dc12e2af (patch)
treeae22345dd2c32ab7c325e1a7d344c8fb5677f33e /Modules/structmodule.c
parent9e3fceb5b3a7e2506ce3b9085a6ede2f6124e4db (diff)
downloadcpython-74679b455fad59a38f4172ae883244c1dc12e2af.zip
cpython-74679b455fad59a38f4172ae883244c1dc12e2af.tar.gz
cpython-74679b455fad59a38f4172ae883244c1dc12e2af.tar.bz2
Support float and double in non-native formats.
These use the ANSI/IEEE standard, which is also used by XDR; so the _xdr module may become obsolete.
Diffstat (limited to 'Modules/structmodule.c')
-rw-r--r--Modules/structmodule.c367
1 files changed, 365 insertions, 2 deletions
diff --git a/Modules/structmodule.c b/Modules/structmodule.c
index 5eb7922..23b820b 100644
--- a/Modules/structmodule.c
+++ b/Modules/structmodule.c
@@ -35,6 +35,7 @@ PERFORMANCE OF THIS SOFTWARE.
character strings, and unsigned numbers */
#include "Python.h"
+#include "mymath.h"
#include <limits.h>
@@ -200,6 +201,274 @@ get_ulong(v, p)
}
+/* Floating point helpers */
+
+/* These use ANSI/IEEE Standard 754-1985 (Standard for Binary Floating
+ Point Arithmetic). See the following URL:
+ http://www.psc.edu/general/software/packages/ieee/ieee.html */
+
+/* XXX Signed zero, infinity, underflow, NaN are not handled quite right? */
+
+static int
+pack_float(x, p, incr)
+ double x; /* The number to pack */
+ char *p; /* Where to pack the high order byte */
+ int incr; /* 1 for big-endian; -1 for little-endian */
+{
+ int s;
+ int e;
+ double fl;
+ long f;
+
+ if (x < 0) {
+ s = 1;
+ x = -x;
+ }
+ else
+ s = 0;
+ fl = frexp(x, &e);
+ /* Normalize fl to be in the range [1.0, 2.0) */
+ if (0.5 <= fl && fl < 1.0) {
+ fl *= 2.0;
+ e--;
+ }
+ else if (fl == 0.0) {
+ e = 0;
+ }
+ else {
+ PyErr_SetString(PyExc_SystemError,
+ "frexp() result out of range");
+ return -1;
+ }
+ e += 127;
+ if (e >= 255) {
+ /* XXX 255 itself is reserved for Inf/NaN */
+ PyErr_SetString(PyExc_OverflowError,
+ "float too large to pack with f format");
+ return -1;
+ }
+ else if (e <= 0) {
+ /* XXX Underflow -- could do better, but who cares? */
+ fl = 0.0;
+ e = 0;
+ }
+ if (fl == 0.0) {
+ f = 0;
+ }
+ else {
+ fl -= 1.0; /* Get rid of leading 1 */
+ fl *= 8388608.0; /* 2**23 */
+ f = (long) floor(fl + 0.5); /* Round */
+ }
+
+ /* First byte */
+ *p = (s<<7) | (e>>1);
+ p += incr;
+
+ /* Second byte */
+ *p = ((e&1)<<7) | (f>>16);
+ p += incr;
+
+ /* Third byte */
+ *p = (f>>8) & 0xFF;
+ p += incr;
+
+ /* Fourth byte */
+ *p = f&0xFF;
+
+ /* Done */
+ return 0;
+}
+
+static int
+pack_double(x, p, incr)
+ double x; /* The number to pack */
+ char *p; /* Where to pack the high order byte */
+ int incr; /* 1 for big-endian; -1 for little-endian */
+{
+ int s;
+ int e;
+ double fl;
+ long fhi, flo;
+
+ if (x < 0) {
+ s = 1;
+ x = -x;
+ }
+ else
+ s = 0;
+ fl = frexp(x, &e);
+ /* Normalize fl to be in the range [1.0, 2.0) */
+ if (0.5 <= fl && fl < 1.0) {
+ fl *= 2.0;
+ e--;
+ }
+ else if (fl == 0.0) {
+ e = 0;
+ }
+ else {
+ PyErr_SetString(PyExc_SystemError,
+ "frexp() result out of range");
+ return -1;
+ }
+ e += 1023;
+ if (e >= 2047) {
+ /* XXX 2047 itself is reserved for Inf/NaN */
+ PyErr_SetString(PyExc_OverflowError,
+ "float too large to pack with d format");
+ return -1;
+ }
+ else if (e <= 0) {
+ /* XXX Underflow -- could do better, but who cares? */
+ fl = 0.0;
+ e = 0;
+ }
+ if (fl == 0.0) {
+ fhi = flo = 0;
+ }
+ else {
+ /* fhi receives the high 28 bits; flo the low 24 bits */
+ fl -= 1.0;
+ fl *= 268435456.0; /* 2**28 */
+ fhi = (long) floor(fl); /* Truncate */
+ fl -= (double)fhi;
+ fl *= 16777216.0; /* 2**24 */
+ flo = (long) floor(fl + 0.5); /* Round */
+ }
+
+ /* First byte */
+ *p = (s<<7) | (e>>4);
+ p += incr;
+
+ /* Second byte */
+ *p = ((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;
+}
+
+static PyObject *
+unpack_float(p, incr)
+ char *p; /* Where the high order byte is */
+ int incr; /* 1 for big-endian; -1 for little-endian */
+{
+ int s;
+ int e;
+ long f;
+ double x;
+
+ /* First byte */
+ s = (*p>>7) & 1;
+ e = (*p & 0x7F) << 1;
+ p += incr;
+
+ /* Second byte */
+ e |= (*p>>7) & 1;
+ f = (*p & 0x7F) << 16;
+ p += incr;
+
+ /* Third byte */
+ f |= (*p & 0xFF) << 8;
+ p += incr;
+
+ /* Fourth byte */
+ f |= *p & 0xFF;
+
+ x = (double)f / 8388608.0;
+
+ /* XXX This sadly ignores Inf/NaN issues */
+ if (e != 0)
+ x = ldexp(1.0 + x, e - 127);
+
+ if (s)
+ x = -x;
+
+ return PyFloat_FromDouble(x);
+}
+
+static PyObject *
+unpack_double(p, incr)
+ char *p; /* Where the high order byte is */
+ int incr; /* 1 for big-endian; -1 for little-endian */
+{
+ int s;
+ int e;
+ long fhi, flo;
+ double x;
+
+ /* First byte */
+ s = (*p>>7) & 1;
+ e = (*p & 0x7F) << 4;
+ p += incr;
+
+ /* Second byte */
+ e |= (*p>>4) & 0xF;
+ fhi = (*p & 0xF) << 24;
+ p += incr;
+
+ /* Third byte */
+ fhi |= (*p & 0xFF) << 16;
+ p += incr;
+
+ /* Fourth byte */
+ fhi |= (*p & 0xFF) << 8;
+ p += incr;
+
+ /* Fifth byte */
+ fhi |= *p & 0xFF;
+ p += incr;
+
+ /* Sixth byte */
+ flo = (*p & 0xFF) << 16;
+ p += incr;
+
+ /* Seventh byte */
+ flo |= (*p & 0xFF) << 8;
+ p += incr;
+
+ /* Eighth byte */
+ flo |= *p & 0xFF;
+ p += incr;
+
+ x = (double)fhi + (double)flo / 16777216.0; /* 2**24 */
+ x /= 268435456.0; /* 2**28 */
+
+ /* XXX This sadly ignores Inf/NaN */
+ if (e != 0)
+ x = ldexp(1.0 + x, e - 1023);
+
+ if (s)
+ x = -x;
+
+ return PyFloat_FromDouble(x);
+}
+
+
/* The translation function for each format character is table driven */
typedef struct _formatdef {
@@ -486,6 +755,22 @@ bu_uint(p, f)
return PyLong_FromLong(x);
}
+static PyObject *
+bu_float(p, f)
+ const char *p;
+ const formatdef *f;
+{
+ return unpack_float(p, 1);
+}
+
+static PyObject *
+bu_double(p, f)
+ const char *p;
+ const formatdef *f;
+{
+ return unpack_double(p, 1);
+}
+
static int
bp_int(p, v, f)
char *p;
@@ -522,6 +807,36 @@ bp_uint(p, v, f)
return 0;
}
+static int
+bp_float(p, v, f)
+ char *p;
+ PyObject *v;
+ const formatdef *f;
+{
+ double x = PyFloat_AsDouble(v);
+ if (x == -1 && PyErr_Occurred()) {
+ PyErr_SetString(StructError,
+ "required argument is not a float");
+ return -1;
+ }
+ return pack_float(x, p, 1);
+}
+
+static int
+bp_double(p, v, f)
+ char *p;
+ PyObject *v;
+ const formatdef *f;
+{
+ double x = PyFloat_AsDouble(v);
+ if (x == -1 && PyErr_Occurred()) {
+ PyErr_SetString(StructError,
+ "required argument is not a float");
+ return -1;
+ }
+ return pack_double(x, p, 1);
+}
+
static formatdef bigendian_table[] = {
{'x', 1, 0, NULL},
{'b', 1, 0, bu_int, bp_int},
@@ -534,7 +849,8 @@ static formatdef bigendian_table[] = {
{'I', 4, 0, bu_uint, bp_uint},
{'l', 4, 0, bu_int, bp_int},
{'L', 4, 0, bu_uint, bp_uint},
- /* No float and double! */
+ {'f', 4, 0, bu_float, bp_float},
+ {'d', 8, 0, bu_double, bp_double},
{0}
};
@@ -572,6 +888,22 @@ lu_uint(p, f)
return PyLong_FromLong(x);
}
+static PyObject *
+lu_float(p, f)
+ const char *p;
+ const formatdef *f;
+{
+ return unpack_float(p+3, -1);
+}
+
+static PyObject *
+lu_double(p, f)
+ const char *p;
+ const formatdef *f;
+{
+ return unpack_double(p+7, -1);
+}
+
static int
lp_int(p, v, f)
char *p;
@@ -608,6 +940,36 @@ lp_uint(p, v, f)
return 0;
}
+static int
+lp_float(p, v, f)
+ char *p;
+ PyObject *v;
+ const formatdef *f;
+{
+ double x = PyFloat_AsDouble(v);
+ if (x == -1 && PyErr_Occurred()) {
+ PyErr_SetString(StructError,
+ "required argument is not a float");
+ return -1;
+ }
+ return pack_float(x, p+3, -1);
+}
+
+static int
+lp_double(p, v, f)
+ char *p;
+ PyObject *v;
+ const formatdef *f;
+{
+ double x = PyFloat_AsDouble(v);
+ if (x == -1 && PyErr_Occurred()) {
+ PyErr_SetString(StructError,
+ "required argument is not a float");
+ return -1;
+ }
+ return pack_double(x, p+7, -1);
+}
+
static formatdef lilendian_table[] = {
{'x', 1, 0, NULL},
{'b', 1, 0, lu_int, lp_int},
@@ -620,7 +982,8 @@ static formatdef lilendian_table[] = {
{'I', 4, 0, lu_uint, lp_uint},
{'l', 4, 0, lu_int, lp_int},
{'L', 4, 0, lu_uint, lp_uint},
- /* No float and double! */
+ {'f', 4, 0, lu_float, lp_float},
+ {'d', 8, 0, lu_double, lp_double},
{0}
};