Commit 1d6e2e18 authored by Mark Dickinson's avatar Mark Dickinson

Issue #7117 (backport py3k float repr) continued:

 - add double endianness detection to configure script
 - add configure-time check to see whether we can use inline
   assembly to get and set x87 control word in configure script
 - add functions to get and set x87 control word in Python/pymath.c
 - add pyport.h logic to determine whether it's safe to use the
   short float repr or not
parent 5e9f6676
......@@ -17,6 +17,9 @@
# undef SIZEOF_VOID_P
# undef SIZEOF__BOOL
# undef WORDS_BIGENDIAN
# undef DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754
# undef DOUBLE_IS_BIG_ENDIAN_IEEE754
# undef DOUBLE_IS_LITTLE_ENDIAN_IEEE754
# undef VA_LIST_IS_ARRAY
# if defined(__LP64__) && defined(__x86_64__)
......@@ -65,6 +68,9 @@
#ifdef __BIG_ENDIAN__
#define WORDS_BIGENDIAN 1
#define DOUBLE_IS_BIG_ENDIAN_IEEE754
#else
#define DOUBLE_IS_LITTLE_ENDIAN_IEEE754
#endif /* __BIG_ENDIAN */
/*
......
......@@ -92,6 +92,11 @@ PyAPI_FUNC(double) _Py_force_double(double);
# endif
#endif
#ifdef HAVE_GCC_ASM_FOR_X87
PyAPI_FUNC(unsigned short) _Py_get_387controlword(void);
PyAPI_FUNC(void) _Py_set_387controlword(unsigned short);
#endif
/* Py_IS_NAN(X)
* Return 1 if float or double arg is a NaN, else 0.
* Caution:
......
......@@ -488,6 +488,80 @@ extern "C" {
errno = 0; \
} while(0)
/* The functions _Py_dg_strtod and _Py_dg_dtoa in Python/dtoa.c (which are
* required to support the short float repr introduced in Python 3.1) require
* that the floating-point unit that's being used for arithmetic operations
* on C doubles is set to use 53-bit precision. It also requires that the
* FPU rounding mode is round-half-to-even, but that's less often an issue.
*
* If your FPU isn't already set to 53-bit precision/round-half-to-even, and
* you want to make use of _Py_dg_strtod and _Py_dg_dtoa, then you should
*
* #define HAVE_PY_SET_53BIT_PRECISION 1
*
* and also give appropriate definitions for the following three macros:
*
* _PY_SET_53BIT_PRECISION_START : store original FPU settings, and
* set FPU to 53-bit precision/round-half-to-even
* _PY_SET_53BIT_PRECISION_END : restore original FPU settings
* _PY_SET_53BIT_PRECISION_HEADER : any variable declarations needed to
* use the two macros above.
*
* The macros are designed to be used within a single C function: see
* Python/pystrtod.c for an example of their use.
*/
/* get and set x87 control word for gcc/x86 */
#ifdef HAVE_GCC_ASM_FOR_X87
#define HAVE_PY_SET_53BIT_PRECISION 1
/* _Py_get/set_387controlword functions are defined in Python/pymath.c */
#define _Py_SET_53BIT_PRECISION_HEADER \
unsigned short old_387controlword, new_387controlword
#define _Py_SET_53BIT_PRECISION_START \
do { \
old_387controlword = _Py_get_387controlword(); \
new_387controlword = (old_387controlword & ~0x0f00) | 0x0200; \
if (new_387controlword != old_387controlword) \
_Py_set_387controlword(new_387controlword); \
} while (0)
#define _Py_SET_53BIT_PRECISION_END \
if (new_387controlword != old_387controlword) \
_Py_set_387controlword(old_387controlword)
#endif
/* default definitions are empty */
#ifndef HAVE_PY_SET_53BIT_PRECISION
#define _Py_SET_53BIT_PRECISION_HEADER
#define _Py_SET_53BIT_PRECISION_START
#define _Py_SET_53BIT_PRECISION_END
#endif
/* If we can't guarantee 53-bit precision, don't use the code
in Python/dtoa.c, but fall back to standard code. This
means that repr of a float will be long (17 sig digits).
Realistically, there are two things that could go wrong:
(1) doubles aren't IEEE 754 doubles, or
(2) we're on x86 with the rounding precision set to 64-bits
(extended precision), and we don't know how to change
the rounding precision.
*/
#if !defined(DOUBLE_IS_LITTLE_ENDIAN_IEEE754) && \
!defined(DOUBLE_IS_BIG_ENDIAN_IEEE754) && \
!defined(DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754)
#define PY_NO_SHORT_FLOAT_REPR
#endif
/* double rounding is symptomatic of use of extended precision on x86. If
we're seeing double rounding, and we don't have any mechanism available for
changing the FPU rounding precision, then don't use Python/dtoa.c. */
#if defined(X87_DOUBLE_ROUNDING) && !defined(HAVE_PY_SET_53BIT_PRECISION)
#define PY_NO_SHORT_FLOAT_REPR
#endif
/* Py_DEPRECATED(version)
* Declare a variable, type, or function deprecated.
* Usage:
......
......@@ -749,4 +749,8 @@ Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */
socket handles greater than FD_SETSIZE */
#define Py_SOCKET_FD_CAN_BE_GE_FD_SETSIZE
/* Define if C doubles are 64-bit IEEE 754 binary format, stored with the
least significant byte first */
#define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1
#endif /* !Py_CONFIG_H */
......@@ -13,6 +13,24 @@ double _Py_force_double(double x)
}
#endif
#ifdef HAVE_GCC_ASM_FOR_X87
/* inline assembly for getting and setting the 387 FPU control word on
gcc/x86 */
unsigned short _Py_get_387controlword(void) {
unsigned short cw;
__asm__ __volatile__ ("fnstcw %0" : "=m" (cw));
return cw;
}
void _Py_set_387controlword(unsigned short cw) {
__asm__ __volatile__ ("fldcw %0" : : "m" (cw));
}
#endif
#ifndef HAVE_HYPOT
double hypot(double x, double y)
{
......
#! /bin/sh
# From configure.in Revision: 74978 .
# From configure.in Revision: 75131 .
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.61 for python 2.7.
#
......@@ -23233,12 +23233,298 @@ echo "${ECHO_T}default LIBC=\"$LIBC\"" >&6; }
fi
# ************************************
# * Check for mathematical functions *
# ************************************
# **************************************************
# * Check for various properties of floating point *
# **************************************************
LIBS_SAVE=$LIBS
LIBS="$LIBS $LIBM"
{ echo "$as_me:$LINENO: checking whether C doubles are little-endian IEEE 754 binary64" >&5
echo $ECHO_N "checking whether C doubles are little-endian IEEE 754 binary64... $ECHO_C" >&6; }
if test "${ac_cv_little_endian_double+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
if test "$cross_compiling" = yes; then
ac_cv_little_endian_double=no
else
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <string.h>
int main() {
double x = 9006104071832581.0;
if (memcmp(&x, "\x05\x04\x03\x02\x01\xff\x3f\x43", 8) == 0)
return 0;
else
return 1;
}
_ACEOF
rm -f conftest$ac_exeext
if { (ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_link") 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && { ac_try='./conftest$ac_exeext'
{ (case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_try") 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; }; then
ac_cv_little_endian_double=yes
else
echo "$as_me: program exited with status $ac_status" >&5
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
( exit $ac_status )
ac_cv_little_endian_double=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
fi
fi
{ echo "$as_me:$LINENO: result: $ac_cv_little_endian_double" >&5
echo "${ECHO_T}$ac_cv_little_endian_double" >&6; }
if test "$ac_cv_little_endian_double" = yes
then
cat >>confdefs.h <<\_ACEOF
#define DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1
_ACEOF
fi
{ echo "$as_me:$LINENO: checking whether C doubles are big-endian IEEE 754 binary64" >&5
echo $ECHO_N "checking whether C doubles are big-endian IEEE 754 binary64... $ECHO_C" >&6; }
if test "${ac_cv_big_endian_double+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
if test "$cross_compiling" = yes; then
ac_cv_big_endian_double=no
else
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <string.h>
int main() {
double x = 9006104071832581.0;
if (memcmp(&x, "\x43\x3f\xff\x01\x02\x03\x04\x05", 8) == 0)
return 0;
else
return 1;
}
_ACEOF
rm -f conftest$ac_exeext
if { (ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_link") 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && { ac_try='./conftest$ac_exeext'
{ (case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_try") 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; }; then
ac_cv_big_endian_double=yes
else
echo "$as_me: program exited with status $ac_status" >&5
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
( exit $ac_status )
ac_cv_big_endian_double=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
fi
fi
{ echo "$as_me:$LINENO: result: $ac_cv_big_endian_double" >&5
echo "${ECHO_T}$ac_cv_big_endian_double" >&6; }
if test "$ac_cv_big_endian_double" = yes
then
cat >>confdefs.h <<\_ACEOF
#define DOUBLE_IS_BIG_ENDIAN_IEEE754 1
_ACEOF
fi
# Some ARM platforms use a mixed-endian representation for doubles.
# While Python doesn't currently have full support for these platforms
# (see e.g., issue 1762561), we can at least make sure that float <-> string
# conversions work.
{ echo "$as_me:$LINENO: checking whether C doubles are ARM mixed-endian IEEE 754 binary64" >&5
echo $ECHO_N "checking whether C doubles are ARM mixed-endian IEEE 754 binary64... $ECHO_C" >&6; }
if test "${ac_cv_mixed_endian_double+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
if test "$cross_compiling" = yes; then
ac_cv_mixed_endian_double=no
else
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <string.h>
int main() {
double x = 9006104071832581.0;
if (memcmp(&x, "\x01\xff\x3f\x43\x05\x04\x03\x02", 8) == 0)
return 0;
else
return 1;
}
_ACEOF
rm -f conftest$ac_exeext
if { (ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_link") 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && { ac_try='./conftest$ac_exeext'
{ (case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_try") 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; }; then
ac_cv_mixed_endian_double=yes
else
echo "$as_me: program exited with status $ac_status" >&5
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
( exit $ac_status )
ac_cv_mixed_endian_double=no
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
fi
fi
{ echo "$as_me:$LINENO: result: $ac_cv_mixed_endian_double" >&5
echo "${ECHO_T}$ac_cv_mixed_endian_double" >&6; }
if test "$ac_cv_mixed_endian_double" = yes
then
cat >>confdefs.h <<\_ACEOF
#define DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754 1
_ACEOF
fi
# The short float repr introduced in Python 3.1 requires the
# correctly-rounded string <-> double conversion functions from
# Python/dtoa.c, which in turn require that the FPU uses 53-bit
# rounding; this is a problem on x86, where the x87 FPU has a default
# rounding precision of 64 bits. For gcc/x86, we try to fix this by
# using inline assembler to get and set the x87 FPU control word.
if test "$GCC" = yes && test -n "`$CC -dM -E - </dev/null | grep i386`"
then
# Check that it's okay to use gcc inline assembler to get and set
# x87 control word. It should be, but you never know...
{ echo "$as_me:$LINENO: checking whether we can use gcc inline assembler to get and set x87 control word" >&5
echo $ECHO_N "checking whether we can use gcc inline assembler to get and set x87 control word... $ECHO_C" >&6; }
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
int
main ()
{
unsigned short cw;
__asm__ __volatile__ ("fnstcw %0" : "=m" (cw));
__asm__ __volatile__ ("fldcw %0" : : "m" (cw));
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
have_gcc_asm_for_x87=yes
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
have_gcc_asm_for_x87=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
{ echo "$as_me:$LINENO: result: $have_gcc_asm_for_x87" >&5
echo "${ECHO_T}$have_gcc_asm_for_x87" >&6; }
if test "$have_gcc_asm_for_x87" = yes
then
cat >>confdefs.h <<\_ACEOF
#define HAVE_GCC_ASM_FOR_X87 1
_ACEOF
fi
fi
# Detect whether system arithmetic is subject to x87-style double
# rounding issues. The result of this test has little meaning on non
......@@ -23327,6 +23613,13 @@ _ACEOF
fi
# ************************************
# * Check for mathematical functions *
# ************************************
LIBS_SAVE=$LIBS
LIBS="$LIBS $LIBM"
# Multiprocessing check for broken sem_getvalue
{ echo "$as_me:$LINENO: checking for broken sem_getvalue" >&5
echo $ECHO_N "checking for broken sem_getvalue... $ECHO_C" >&6; }
......
......@@ -3240,12 +3240,107 @@ else AC_MSG_ERROR([proper usage is --with-libc=STRING])
fi],
[AC_MSG_RESULT(default LIBC="$LIBC")])
# ************************************
# * Check for mathematical functions *
# ************************************
# **************************************************
# * Check for various properties of floating point *
# **************************************************
LIBS_SAVE=$LIBS
LIBS="$LIBS $LIBM"
AC_MSG_CHECKING(whether C doubles are little-endian IEEE 754 binary64)
AC_CACHE_VAL(ac_cv_little_endian_double, [
AC_TRY_RUN([
#include <string.h>
int main() {
double x = 9006104071832581.0;
if (memcmp(&x, "\x05\x04\x03\x02\x01\xff\x3f\x43", 8) == 0)
return 0;
else
return 1;
}
],
ac_cv_little_endian_double=yes,
ac_cv_little_endian_double=no,
ac_cv_little_endian_double=no)])
AC_MSG_RESULT($ac_cv_little_endian_double)
if test "$ac_cv_little_endian_double" = yes
then
AC_DEFINE(DOUBLE_IS_LITTLE_ENDIAN_IEEE754, 1,
[Define if C doubles are 64-bit IEEE 754 binary format, stored
with the least significant byte first])
fi
AC_MSG_CHECKING(whether C doubles are big-endian IEEE 754 binary64)
AC_CACHE_VAL(ac_cv_big_endian_double, [
AC_TRY_RUN([
#include <string.h>
int main() {
double x = 9006104071832581.0;
if (memcmp(&x, "\x43\x3f\xff\x01\x02\x03\x04\x05", 8) == 0)
return 0;
else
return 1;
}
],
ac_cv_big_endian_double=yes,
ac_cv_big_endian_double=no,
ac_cv_big_endian_double=no)])
AC_MSG_RESULT($ac_cv_big_endian_double)
if test "$ac_cv_big_endian_double" = yes
then
AC_DEFINE(DOUBLE_IS_BIG_ENDIAN_IEEE754, 1,
[Define if C doubles are 64-bit IEEE 754 binary format, stored
with the most significant byte first])
fi
# Some ARM platforms use a mixed-endian representation for doubles.
# While Python doesn't currently have full support for these platforms
# (see e.g., issue 1762561), we can at least make sure that float <-> string
# conversions work.
AC_MSG_CHECKING(whether C doubles are ARM mixed-endian IEEE 754 binary64)
AC_CACHE_VAL(ac_cv_mixed_endian_double, [
AC_TRY_RUN([
#include <string.h>
int main() {
double x = 9006104071832581.0;
if (memcmp(&x, "\x01\xff\x3f\x43\x05\x04\x03\x02", 8) == 0)
return 0;
else
return 1;
}
],
ac_cv_mixed_endian_double=yes,
ac_cv_mixed_endian_double=no,
ac_cv_mixed_endian_double=no)])
AC_MSG_RESULT($ac_cv_mixed_endian_double)
if test "$ac_cv_mixed_endian_double" = yes
then
AC_DEFINE(DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754, 1,
[Define if C doubles are 64-bit IEEE 754 binary format, stored
in ARM mixed-endian order (byte order 45670123)])
fi
# The short float repr introduced in Python 3.1 requires the
# correctly-rounded string <-> double conversion functions from
# Python/dtoa.c, which in turn require that the FPU uses 53-bit
# rounding; this is a problem on x86, where the x87 FPU has a default
# rounding precision of 64 bits. For gcc/x86, we try to fix this by
# using inline assembler to get and set the x87 FPU control word.
if test "$GCC" = yes && test -n "`$CC -dM -E - </dev/null | grep i386`"
then
# Check that it's okay to use gcc inline assembler to get and set
# x87 control word. It should be, but you never know...
AC_MSG_CHECKING(whether we can use gcc inline assembler to get and set x87 control word)
AC_TRY_COMPILE([], [
unsigned short cw;
__asm__ __volatile__ ("fnstcw %0" : "=m" (cw));
__asm__ __volatile__ ("fldcw %0" : : "m" (cw));
],
[have_gcc_asm_for_x87=yes], [have_gcc_asm_for_x87=no])
AC_MSG_RESULT($have_gcc_asm_for_x87)
if test "$have_gcc_asm_for_x87" = yes
then
AC_DEFINE(HAVE_GCC_ASM_FOR_X87, 1,
[Define if we can use gcc inline assembler to get and set x87 control word])
fi
fi
# Detect whether system arithmetic is subject to x87-style double
# rounding issues. The result of this test has little meaning on non
......@@ -3284,6 +3379,13 @@ then
[Define if arithmetic is subject to x87-style double rounding issue])
fi
# ************************************
# * Check for mathematical functions *
# ************************************
LIBS_SAVE=$LIBS
LIBS="$LIBS $LIBM"
# Multiprocessing check for broken sem_getvalue
AC_MSG_CHECKING(for broken sem_getvalue)
AC_CACHE_VAL(ac_cv_broken_sem_getvalue,
......
......@@ -18,6 +18,18 @@
/* Define if you have the Mach cthreads package */
#undef C_THREADS
/* Define if C doubles are 64-bit IEEE 754 binary format, stored in ARM
mixed-endian order (byte order 45670123) */
#undef DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754
/* Define if C doubles are 64-bit IEEE 754 binary format, stored with the most
significant byte first */
#undef DOUBLE_IS_BIG_ENDIAN_IEEE754
/* Define if C doubles are 64-bit IEEE 754 binary format, stored with the
least significant byte first */
#undef DOUBLE_IS_LITTLE_ENDIAN_IEEE754
/* Define if --enable-ipv6 is specified */
#undef ENABLE_IPV6
......@@ -240,6 +252,10 @@
/* Define to 1 if you have the `gamma' function. */
#undef HAVE_GAMMA
/* Define if we can use gcc inline assembler to get and set x87 control word
*/
#undef HAVE_GCC_ASM_FOR_X87
/* Define if you have the getaddrinfo function. */
#undef HAVE_GETADDRINFO
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment