From 823fbd0e885aa28aafb17746c8040a3c65fbb893 Mon Sep 17 00:00:00 2001 From: Raymond Lu Date: Tue, 20 Jun 2006 09:45:37 -0500 Subject: [svn-r12425] Purpose: Bug fix Description: Data conversion from long double to (unsigned) long long returns some incorrect values on Mac OS 10.4 and SGI IRIX64 6.5. The conversions start to go wrong when the long double is 20041683600089727.779961 (0x4351ccf385ebc8a0bfcc2a...). If adjusting the values higher by assigning 0x...c8a0cf... or 0x...c8a0df..., the converted values go wild. Solution: Detect this error in configure.in and disable compiler conversions from long double to (unsigned) long long for Mac OS 10.4 and IRIX64 6.5. Platforms tested: h5committest, Mac OS 10.4, and IRIX64 6.5. --- configure | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ configure.in | 63 ++++++++++++++++++++++++++++++++++ src/H5Tpkg.h | 13 +++---- src/H5config.h.in | 4 +++ test/dt_arith.c | 22 ++++++++++-- 5 files changed, 193 insertions(+), 9 deletions(-) diff --git a/configure b/configure index ec9fb85..78468ad 100755 --- a/configure +++ b/configure @@ -51683,6 +51683,106 @@ else echo "${ECHO_T}no" >&6 fi +echo "$as_me:$LINENO: checking if correctly converting long double to (unsigned) long long values" >&5 +echo $ECHO_N "checking if correctly converting long double to (unsigned) long long values... $ECHO_C" >&6 + +if test ${ac_cv_sizeof_long_double} = 0; then + hdf5_ldouble_to_llong_accurate=${hdf5_ldouble_to_llong_accurate=no} +else + if test "${hdf5_ldouble_to_llong_accurate+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run test program while cross compiling +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + int main(void) + { + long double ld = 20041683600089727.779961L; + long long ll; + unsigned long long ull; + unsigned char s[16]; + int ret = 0; + + if(sizeof(long double) == 16) { + /*make sure the long double type is the same as the failing type + *which has 16 bytes in size and 11 bits of exponent. If it is, + *the bit sequence should be like below. It's not + *a decent way to check but this info isn't available. */ + memcpy(s, &ld, 16); + if(s[0]==0x43 && s[1]==0x51 && s[2]==0xcc && s[3]==0xf3 && + s[4]==0x85 && s[5]==0xeb && s[6]==0xc8 && s[7]==0xa0 && + s[8]==0xbf && s[9]==0xcc && s[10]==0x2a && s[11]==0x3c) { + + /*slightly adjust the bit sequence (s[8]=0xdf). The converted + *values will go wild on Mac OS 10.4 and IRIX64 6.5.*/ + s[0]=0x43; s[1]=0x51; s[2]=0xcc; s[3]=0xf3; + s[4]=0x85; s[5]=0xeb; s[6]=0xc8; s[7]=0xa0; + s[8]=0xdf; s[9]=0xcc; s[10]=0x2a; s[11]=0x3c; + s[12]=0x3d; s[13]=0x85; s[14]=0x56; s[15]=0x20; + + memcpy(&ld, s, 16); + ll = (long long)ld; + ull = (unsigned long long)ld; + + if(ll != 20041683600089728 || ull != 20041683600089728) + ret = 1; + } + } + done: + exit(ret); + } + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + hdf5_ldouble_to_llong_accurate=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 ) +hdf5_ldouble_to_llong_accurate=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi + +fi + +if test ${hdf5_ldouble_to_llong_accurate} = "yes"; then + +cat >>confdefs.h <<\_ACEOF +#define LDOUBLE_TO_LLONG_ACCURATE 1 +_ACEOF + + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + H5_VERSION="`cut -d' ' -f3 $srcdir/README.txt | head -1`" diff --git a/configure.in b/configure.in index 7afc0fd..c8bd31d 100644 --- a/configure.in +++ b/configure.in @@ -3094,6 +3094,69 @@ else fi dnl ---------------------------------------------------------------------- +dnl Set the flag to indicate that the machine can accurately convert +dnl 'long double' to '(unsigned) long long' values. (This flag should be set for +dnl all machines, except for Mac OS 10.4 and SGI IRIX64 6.5. When the bit sequence +dnl of long double is 0x4351ccf385ebc8a0bfcc2a3c..., the values of (unsigned)long long +dnl start to go wrong on these two machines. Adjusting it higher to +dnl 0x4351ccf385ebc8a0dfcc... or 0x4351ccf385ebc8a0ffcc... will make the converted +dnl values wildly wrong. This test detects this wrong behavior and disable the test. +dnl +AC_MSG_CHECKING([if correctly converting long double to (unsigned) long long values]) + +if test ${ac_cv_sizeof_long_double} = 0; then + hdf5_ldouble_to_llong_accurate=${hdf5_ldouble_to_llong_accurate=no} +else + AC_CACHE_VAL([hdf5_ldouble_to_llong_accurate], + [AC_TRY_RUN([ + int main(void) + { + long double ld = 20041683600089727.779961L; + long long ll; + unsigned long long ull; + unsigned char s[16]; + int ret = 0; + + if(sizeof(long double) == 16) { + /*make sure the long double type is the same as the failing type + *which has 16 bytes in size and 11 bits of exponent. If it is, + *the bit sequence should be like below. It's not + *a decent way to check but this info isn't available. */ + memcpy(s, &ld, 16); + if(s[0]==0x43 && s[1]==0x51 && s[2]==0xcc && s[3]==0xf3 && + s[4]==0x85 && s[5]==0xeb && s[6]==0xc8 && s[7]==0xa0 && + s[8]==0xbf && s[9]==0xcc && s[10]==0x2a && s[11]==0x3c) { + + /*slightly adjust the bit sequence (s[8]=0xdf). The converted + *values will go wild on Mac OS 10.4 and IRIX64 6.5.*/ + s[0]=0x43; s[1]=0x51; s[2]=0xcc; s[3]=0xf3; + s[4]=0x85; s[5]=0xeb; s[6]=0xc8; s[7]=0xa0; + s[8]=0xdf; s[9]=0xcc; s[10]=0x2a; s[11]=0x3c; + s[12]=0x3d; s[13]=0x85; s[14]=0x56; s[15]=0x20; + + memcpy(&ld, s, 16); + ll = (long long)ld; + ull = (unsigned long long)ld; + + if(ll != 20041683600089728 || ull != 20041683600089728) + ret = 1; + } + } + done: + exit(ret); + } + ], [hdf5_ldouble_to_llong_accurate=yes], [hdf5_ldouble_to_llong_accurate=no],)]) +fi + +if test ${hdf5_ldouble_to_llong_accurate} = "yes"; then + AC_DEFINE([LDOUBLE_TO_LLONG_ACCURATE], [1], + [Define if your system can convert long double to (unsigned) long long values correctly.]) + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi + +dnl ---------------------------------------------------------------------- dnl Set some variables for general configuration information to be saved dnl and installed with the libraries. dnl diff --git a/src/H5Tpkg.h b/src/H5Tpkg.h index 522329b..9a5ab33 100644 --- a/src/H5Tpkg.h +++ b/src/H5Tpkg.h @@ -138,10 +138,10 @@ #endif /* Define an internal macro for converting long double to long long. SGI compilers give some incorrect - * conversions. HP-UX 11.00 compiler generates floating exception. The hard conversion on Windows - * .NET 2003 has a bug and gives wrong exception value. */ + * conversions. Mac OS 10.4 gives incorrect conversions. HP-UX 11.00 compiler generates floating exception. + * The hard conversion on Windows .NET 2003 has a bug and gives wrong exception value. */ #if (H5_WANT_DATA_ACCURACY && !H5_HW_FP_TO_LLONG_NOT_WORKS && H5_LDOUBLE_TO_INTEGER_ACCURATE && \ - H5_LDOUBLE_TO_INTEGER_WORKS) || \ + H5_LDOUBLE_TO_INTEGER_WORKS && H5_LDOUBLE_TO_LLONG_ACCURATE) || \ (!H5_WANT_DATA_ACCURACY && !H5_HW_FP_TO_LLONG_NOT_WORKS && H5_LDOUBLE_TO_INTEGER_WORKS) #define H5T_CONV_INTERNAL_LDOUBLE_LLONG 1 #endif @@ -156,10 +156,11 @@ #define H5T_CONV_INTERNAL_FP_ULLONG 0 #endif -/* Define an internal macro for converting long double to all integers. SGI compilers give some incorrect - * conversions. HP-UX 11.00 compiler generates floating exception. */ +/* Define an internal macro for converting long double to unsigned long long. SGI compilers give some + * incorrect conversions. Mac OS 10.4 gives incorrect conversions. HP-UX 11.00 compiler generates + * floating exception. */ #if (H5_WANT_DATA_ACCURACY && H5_LDOUBLE_TO_INTEGER_ACCURATE && H5_LDOUBLE_TO_INTEGER_WORKS && \ - H5_FP_TO_ULLONG_ACCURATE && H5_FP_TO_ULLONG_RIGHT_MAXIMUM) || \ + H5_FP_TO_ULLONG_ACCURATE && H5_FP_TO_ULLONG_RIGHT_MAXIMUM && H5_LDOUBLE_TO_LLONG_ACCURATE) || \ (!H5_WANT_DATA_ACCURACY && H5_LDOUBLE_TO_INTEGER_WORKS) #define H5T_CONV_INTERNAL_LDOUBLE_ULLONG 1 #else diff --git a/src/H5config.h.in b/src/H5config.h.in index 15bb630..5497037 100644 --- a/src/H5config.h.in +++ b/src/H5config.h.in @@ -383,6 +383,10 @@ /* Define if your system can convert from long double to integer values. */ #undef LDOUBLE_TO_INTEGER_WORKS +/* Define if your system can convert long double to (unsigned) long long + values correctly. */ +#undef LDOUBLE_TO_LLONG_ACCURATE + /* Define if your system can convert long double to unsigned int values correctly. */ #undef LDOUBLE_TO_UINT_ACCURATE diff --git a/test/dt_arith.c b/test/dt_arith.c index 2df3fd9..5569446 100644 --- a/test/dt_arith.c +++ b/test/dt_arith.c @@ -5120,10 +5120,26 @@ run_fp_int_conv(const char *name) #endif #if H5_SIZEOF_LONG_LONG!=H5_SIZEOF_LONG && H5_SIZEOF_LONG_DOUBLE!=0 +#ifdef H5_LDOUBLE_TO_LLONG_ACCURATE nerrors += test_conv_int_fp(name, test_values, H5T_NATIVE_LDOUBLE, H5T_NATIVE_LLONG); -#ifdef H5_FP_TO_ULLONG_RIGHT_MAXIMUM +#else /*H5_LDOUBLE_TO_LLONG_ACCURATE*/ + { + char str[256]; /*string */ + + sprintf(str, "Testing %s %s -> %s conversions", + name, "long double", "long long"); + printf("%-70s", str); + SKIPPED(); +#if H5_SIZEOF_LONG_DOUBLE!=0 + HDputs(" Test skipped due to hardware conversion error."); +#else + HDputs(" Test skipped due to disabled long double."); +#endif + } +#endif /*H5_LDOUBLE_TO_LLONG_ACCURATE*/ +#if H5_FP_TO_ULLONG_RIGHT_MAXIMUM && H5_LDOUBLE_TO_LLONG_ACCURATE nerrors += test_conv_int_fp(name, test_values, H5T_NATIVE_LDOUBLE, H5T_NATIVE_ULLONG); -#else /*H5_FP_TO_ULLONG_RIGHT_MAXIMUM*/ +#else /*H5_FP_TO_ULLONG_RIGHT_MAXIMUM && H5_LDOUBLE_TO_LLONG_ACCURATE*/ { char str[256]; /*string */ @@ -5137,7 +5153,7 @@ run_fp_int_conv(const char *name) HDputs(" Test skipped due to disabled long double."); #endif } -#endif /*H5_FP_TO_ULLONG_RIGHT_MAXIMUM*/ +#endif /*H5_FP_TO_ULLONG_RIGHT_MAXIMUM && H5_LDOUBLE_TO_LLONG_ACCURATE*/ #endif #endif #else /*H5_LDOUBLE_TO_INTEGER_WORKS && H5_LDOUBLE_TO_INTEGER_ACCURATE*/ -- cgit v0.12