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.
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
index 6e65756..3cbc98a 100644
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -904,3 +904,316 @@
 		}
 	}
 }
+
+/*----------------------------------------------------------------------------
+ * _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;
+}