[Python-checkins] python/dist/src/Objects floatobject.c,2.120,2.121

tim_one@users.sourceforge.net tim_one@users.sourceforge.net
Thu, 20 Mar 2003 12:53:35 -0800


Update of /cvsroot/python/python/dist/src/Objects
In directory sc8-pr-cvs1:/tmp/cvs-serv6732/python/Objects

Modified Files:
	floatobject.c 
Log Message:
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.


Index: floatobject.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Objects/floatobject.c,v
retrieving revision 2.120
retrieving revision 2.121
diff -C2 -d -r2.120 -r2.121
*** floatobject.c	29 Jan 2003 17:58:45 -0000	2.120
--- floatobject.c	20 Mar 2003 20:53:32 -0000	2.121
***************
*** 905,906 ****
--- 905,1219 ----
  	}
  }
+ 
+ /*----------------------------------------------------------------------------
+  * _PyFloat_{Pack,Unpack}{4,8}.  See floatobject.h.
+  *
+  * TODO:  On platforms that use the standard IEEE-754 single and double
+  * formats natively, these routines could simply copy the bytes.
+  */
+ 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;
+ 
+ 	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 = (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;
+ 
+  Overflow:
+ 	PyErr_SetString(PyExc_OverflowError,
+ 			"float too large to pack with f format");
+ 	return -1;
+ }
+ 
+ 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 (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;
+ 	}
+ 
+ 	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;
+ 		}
+ 	}
+ 
+ 	/* 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;
+ }
+ 
+ double
+ _PyFloat_Unpack4(const unsigned char *p, int le)
+ {
+ 	unsigned char sign;
+ 	int e;
+ 	unsigned int f;
+ 	double x;
+ 	int 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;
+ 
+ 	/* 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;
+ 	}
+ 	x = ldexp(x, e);
+ 
+ 	if (sign)
+ 		x = -x;
+ 
+ 	return x;
+ }
+ 
+ double
+ _PyFloat_Unpack8(const unsigned char *p, int le)
+ {
+ 	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;
+ 
+ 	/* 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;
+ 
+ 	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 (sign)
+ 		x = -x;
+ 
+ 	return x;
+ }