Index: Objects/floatobject.c =================================================================== --- Objects/floatobject.c (revision 69588) +++ Objects/floatobject.c (working copy) @@ -1669,9 +1669,18 @@ /* this is for the benefit of the pack/unpack routines below */ typedef enum { - unknown_format, ieee_big_endian_format, ieee_little_endian_format + unknown_format, + ieee_big_endian_format, + ieee_little_endian_format, + ieee_arm_mixed_endian_format } float_format_type; +/* byte order of a C double for each of the recognised IEEE formats */ + +static const unsigned char BIG_ENDIAN_BYTEORDER[8] = {7,6,5,4,3,2,1,0}; +static const unsigned char LITTLE_ENDIAN_BYTEORDER[8] = {0,1,2,3,4,5,6,7}; +static const unsigned char ARM_MIXED_ENDIAN_BYTEORDER[8] = {4,5,6,7,0,1,2,3}; + static float_format_type double_format, float_format; static float_format_type detected_double_format, detected_float_format; @@ -1700,7 +1709,7 @@ "'double' or 'float'"); return NULL; } - + switch (r) { case unknown_format: return PyString_FromString("unknown"); @@ -1708,6 +1717,8 @@ return PyString_FromString("IEEE, little-endian"); case ieee_big_endian_format: return PyString_FromString("IEEE, big-endian"); + case ieee_arm_mixed_endian_format: + return PyString_FromString("IEEE, ARM mixed-endian"); default: Py_FatalError("insane float_format or double_format"); return NULL; @@ -1721,8 +1732,9 @@ "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."); +"'unknown', 'IEEE, big-endian', 'IEEE, little-endian' or\n" +"'IEEE, ARM mixed-endian' best describes the format of floating-point\n" +"numbers used by the C type named by typestr."); static PyObject * float_setformat(PyTypeObject *v, PyObject* args) @@ -1750,7 +1762,7 @@ "be 'double' or 'float'"); return NULL; } - + if (strcmp(format, "unknown") == 0) { f = unknown_format; } @@ -1760,11 +1772,15 @@ else if (strcmp(format, "IEEE, big-endian") == 0) { f = ieee_big_endian_format; } + else if (strcmp(format, "IEEE, ARM mixed-endian") == 0 && + p == &double_format) { + f = ieee_arm_mixed_endian_format; + } else { PyErr_SetString(PyExc_ValueError, "__setformat__() argument 2 must be " - "'unknown', 'IEEE, little-endian' or " - "'IEEE, big-endian'"); + "'unknown', 'IEEE, little-endian', " + "'IEEE, big-endian' or 'IEEE, ARM mixed-endian'"); return NULL; } @@ -1787,8 +1803,10 @@ "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" +"'IEEE, big-endian', 'IEEE, little-endian' or 'IEEE, ARM mixed-endian'\n" +"and in addition can only be one of the last three if it appears to\n" +"match the underlying C reality. Note that the ARM mixed-endian\n" +"format can only be set for the 'double' type, not for 'float'.\n" "\n" "Overrides the automatic determination of C-level floating point type.\n" "This affects how floats are converted to and from binary strings."); @@ -1983,8 +2001,13 @@ 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. */ + method. + Addendum: we also attempt to detect the mixed-endian IEEE format + used by the ARM old ABI (OABI) and also used by the FPA + floating-point unit on some older ARM processors. + */ + #if SIZEOF_DOUBLE == 8 { double x = 9006104071832581.0; @@ -1992,7 +2015,9 @@ 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 + else if (memcmp(&x, "\x01\xff\x3f\x43\x05\x04\x03\x02", 8) == 0) + detected_double_format = ieee_arm_mixed_endian_format; + else detected_double_format = unknown_format; } #else @@ -2006,7 +2031,7 @@ 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 + else detected_float_format = unknown_format; } #else @@ -2113,6 +2138,7 @@ /*---------------------------------------------------------------------------- * _PyFloat_{Pack,Unpack}{4,8}. See floatobject.h. */ + int _PyFloat_Pack4(double x, unsigned char *p, int le) { @@ -2331,18 +2357,32 @@ } else { const char *s = (char*)&x; - int i, incr = 1; + int i; + const unsigned char *byteorder; - if ((double_format == ieee_little_endian_format && !le) - || (double_format == ieee_big_endian_format && le)) { - p += 7; - incr = -1; + switch (double_format) { + case ieee_little_endian_format: + byteorder = LITTLE_ENDIAN_BYTEORDER; + break; + case ieee_big_endian_format: + byteorder = BIG_ENDIAN_BYTEORDER; + break; + case ieee_arm_mixed_endian_format: + byteorder = ARM_MIXED_ENDIAN_BYTEORDER; + break; + default: + Py_FatalError("insane float_format or double_format"); + return -1; } - - for (i = 0; i < 8; i++) { - *p = *s++; - p += incr; + + if (le) { + for (i = 0; i < 8; i++) + p[byteorder[i]] = *s++; } + else { + for (i = 0; i < 8; i++) + p[7-byteorder[i]] = *s++; + } return 0; } } @@ -2422,7 +2462,7 @@ } return x; - } + } } double @@ -2443,7 +2483,7 @@ /* First byte */ sign = (*p >> 7) & 1; e = (*p & 0x7F) << 4; - + p += incr; /* Second byte */ @@ -2500,22 +2540,33 @@ } else { double x; + char *s = (char*)&x; + const unsigned char *byteorder; + int i; - 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++; - } - memcpy(&x, buf, 8); + switch (double_format) { + case ieee_little_endian_format: + byteorder = LITTLE_ENDIAN_BYTEORDER; + break; + case ieee_big_endian_format: + byteorder = BIG_ENDIAN_BYTEORDER; + break; + case ieee_arm_mixed_endian_format: + byteorder = ARM_MIXED_ENDIAN_BYTEORDER; + break; + default: + Py_FatalError("insane float_format or double_format"); + return -1.0; } + + if (le) { + for (i=0; i<8; i++) + *s++ = p[byteorder[i]]; + } else { - memcpy(&x, p, 8); + for (i=0; i<8; i++) + *s++ = p[7-byteorder[i]]; } - return x; } }