--- Python-2.7.2.old/Objects/floatobject.c 2011-06-11 18:46:27.000000000 +0300 +++ Python-2.7.2/Objects/floatobject.c 2012-03-13 12:42:54.000000000 +0200 @@ -1835,9 +1835,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; @@ -1874,6 +1883,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; @@ -1887,8 +1898,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) @@ -1926,13 +1938,16 @@ 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; - } if (f != unknown_format && f != detected) { @@ -1953,8 +1968,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."); @@ -2149,7 +2166,12 @@ 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 { @@ -2158,6 +2180,8 @@ 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 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; } @@ -2503,17 +2527,31 @@ } else { const char *s = (char*)&x; - int i, incr = 1; - - if ((double_format == ieee_little_endian_format && !le) - || (double_format == ieee_big_endian_format && le)) { - p += 7; - incr = -1; + int i; + const unsigned char *byteorder; + + 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; } @@ -2672,22 +2710,33 @@ } else { double x; - - 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); + char *s = (char*)&x; + const unsigned char *byteorder; + int i; + + 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; } }