New private API functions _PyFloat_{Pack,Unpack}(4,8}. This is a

refactoring to get all the duplicates of this delicate code out of the
cPickle and struct modules.
This commit is contained in:
Tim Peters 2003-03-20 20:53:32 +00:00
parent d50ade68ec
commit 9905b943f7
4 changed files with 382 additions and 446 deletions

View file

@ -185,301 +185,27 @@ get_ulonglong(PyObject *v, unsigned LONG_LONG *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 Inf/NaN are not handled quite right (but underflow is!) */
static int
pack_float(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 f;
long fbits;
if (x < 0) {
s = 1;
x = -x;
}
else
s = 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 = (long) floor(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 = (s<<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;
Overflow:
PyErr_SetString(PyExc_OverflowError,
"float too large to pack with f format");
return -1;
}
static int
pack_double(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 f;
long fhi, flo;
if (x < 0) {
s = 1;
x = -x;
}
else
s = 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 >= 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 = (long) floor(f); /* Truncate */
assert(fhi < 268435456);
f -= (double)fhi;
f *= 16777216.0; /* 2**24 */
flo = (long) floor(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 = (s<<7) | (e>>4);
p += incr;
/* Second byte */
*p = (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;
}
static PyObject *
unpack_float(const char *p, /* Where the high order byte is */
int incr) /* 1 for big-endian; -1 for little-endian */
unpack_float(const char *p, /* start of 4-byte string */
int le) /* true for little-endian, false for big-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)
e = -126;
else {
x += 1.0;
e -= 127;
}
x = ldexp(x, e);
if (s)
x = -x;
x = _PyFloat_Unpack4((unsigned char *)p, le);
if (x == -1.0 && PyErr_Occurred())
return NULL;
return PyFloat_FromDouble(x);
}
static PyObject *
unpack_double(const char *p, /* Where the high order byte is */
int incr) /* 1 for big-endian; -1 for little-endian */
unpack_double(const char *p, /* start of 8-byte string */
int le) /* true for little-endian, false for big-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)
e = -1022;
else {
x += 1.0;
e -= 1023;
}
x = ldexp(x, e);
if (s)
x = -x;
x = _PyFloat_Unpack8((unsigned char *)p, le);
if (x == -1.0 && PyErr_Occurred())
return NULL;
return PyFloat_FromDouble(x);
}
@ -887,13 +613,13 @@ bu_ulonglong(const char *p, const formatdef *f)
static PyObject *
bu_float(const char *p, const formatdef *f)
{
return unpack_float(p, 1);
return unpack_float(p, 0);
}
static PyObject *
bu_double(const char *p, const formatdef *f)
{
return unpack_double(p, 1);
return unpack_double(p, 0);
}
static int
@ -967,7 +693,7 @@ bp_float(char *p, PyObject *v, const formatdef *f)
"required argument is not a float");
return -1;
}
return pack_float(x, p, 1);
return _PyFloat_Pack4(x, (unsigned char *)p, 0);
}
static int
@ -979,7 +705,7 @@ bp_double(char *p, PyObject *v, const formatdef *f)
"required argument is not a float");
return -1;
}
return pack_double(x, p, 1);
return _PyFloat_Pack8(x, (unsigned char *)p, 0);
}
static formatdef bigendian_table[] = {
@ -1053,13 +779,13 @@ lu_ulonglong(const char *p, const formatdef *f)
static PyObject *
lu_float(const char *p, const formatdef *f)
{
return unpack_float(p+3, -1);
return unpack_float(p, 1);
}
static PyObject *
lu_double(const char *p, const formatdef *f)
{
return unpack_double(p+7, -1);
return unpack_double(p, 1);
}
static int
@ -1133,7 +859,7 @@ lp_float(char *p, PyObject *v, const formatdef *f)
"required argument is not a float");
return -1;
}
return pack_float(x, p+3, -1);
return _PyFloat_Pack4(x, (unsigned char *)p, 1);
}
static int
@ -1145,7 +871,7 @@ lp_double(char *p, PyObject *v, const formatdef *f)
"required argument is not a float");
return -1;
}
return pack_double(x, p+7, -1);
return _PyFloat_Pack8(x, (unsigned char *)p, 1);
}
static formatdef lilendian_table[] = {