From a985c4c52596924c1cb5020ac514163c46e4ff79 Mon Sep 17 00:00:00 2001 From: Raymond Lu Date: Tue, 11 Jan 2005 13:17:01 -0500 Subject: [PATCH] [svn-r9807] Purpose: bug fix Description: For FreeBSD (sleipnir), when GNU compilers do conversion from unsigned long long to long double, the last 2 bytes of mantissa are lost. The impact of precision loss isn't significant. Solution: Detect this case on FreeBSD in configure, ignore it in dtypes.c test instead of return failure. Platforms tested: sleipnir, fuss, modi4. These systems are mainly concerned. --- configure | 90 ++++++++++++++++++++++++++++++++++++++++++++++- configure.in | 57 +++++++++++++++++++++++++++++- src/H5config.h.in | 4 +++ test/dtypes.c | 15 ++++++++ 4 files changed, 164 insertions(+), 2 deletions(-) diff --git a/configure b/configure index 91c41c713a..708ff29361 100755 --- a/configure +++ b/configure @@ -33808,7 +33808,7 @@ else unsigned long long l2 = (unsigned long long)d1; unsigned long long l3 = (unsigned long long)d2; unsigned long long l4; - unsigned long long l5 = 0x7fffffffffffffffLLU; + unsigned long long l5 = 0x7fffffffffffffffULL; int ret = 0; if(l1 <= l5 || l2 <= l5 || l3 <= l5) @@ -33883,6 +33883,94 @@ else echo "${ECHO_T}no" >&6 fi +echo "$as_me:$LINENO: checking if converting unsigned long long to long double with precision work" >&5 +echo $ECHO_N "checking if converting unsigned long long to long double with precision work... $ECHO_C" >&6 +if test "${hdf5_cv_ullong_to_ldouble_precision_works+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" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +#include "confdefs.h" + + int main(void) + { + unsigned long long l = 0xa601e80bda85fcefULL; + long double ld; + unsigned char *c1, *c2; + int endian, size; + int tst_value = 1; + int i; + int ret = 0; + + /* Determine this system's endianess */ + c1 = (unsigned char*)calloc(1, sizeof(int)); + memcpy(c1, &tst_value, sizeof(int)); + if(c1[0]==1) + endian = 0; /* little endian */ + else + endian = 1; /* big endian */ + + memset(&ld, 0, 12); + ld = (long double)l; + + size = sizeof(long double); + c2 = (unsigned char*)malloc(size); + memcpy(c2, &ld, size); + + /* Test if the last 2 bytes of mantissa are lost. Mainly for FreeBSD on Intel + * architecture(sleipnir) where it happens. */ + if(endian==0 && c2[0]==0 && c2[1]==0) /*little endian*/ + ret = 1; + + free(c1); + free(c2); +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_cv_ullong_to_ldouble_precision_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +( exit $ac_status ) +hdf5_cv_ullong_to_ldouble_precision_works=no +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi + + +if test ${hdf5_cv_ullong_to_ldouble_precision_works} = "yes"; then + +cat >>confdefs.h <<\_ACEOF +#define ULLONG_TO_LDOUBLE_PRECISION_WORKS 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 f6687176dd..3dfacbc551 100644 --- a/configure.in +++ b/configure.in @@ -2685,7 +2685,7 @@ AC_CACHE_VAL([hdf5_cv_fp_to_ullong_right_maximum], unsigned long long l2 = (unsigned long long)d1; unsigned long long l3 = (unsigned long long)d2; unsigned long long l4; - unsigned long long l5 = 0x7fffffffffffffffLLU; + unsigned long long l5 = 0x7fffffffffffffffULL; int ret = 0; if(l1 <= l5 || l2 <= l5 || l3 <= l5) @@ -2727,6 +2727,61 @@ else AC_MSG_RESULT([no]) fi +dnl ---------------------------------------------------------------------- +dnl Set the flag to indicate that the machine can convert from +dnl 'unsigned long long' to 'long double' without precision loss. +dnl (This flag should be set for all machines, except for FreeBSD(sleipnir) +dnl where the last 2 bytes of mantissa are lost when compiler tries to do +dnl the conversion.) +dnl +AC_MSG_CHECKING([if converting unsigned long long to long double with precision work]) +AC_CACHE_VAL([hdf5_cv_ullong_to_ldouble_precision_works], + [AC_TRY_RUN([ + int main(void) + { + unsigned long long l = 0xa601e80bda85fcefULL; + long double ld; + unsigned char *c1, *c2; + int endian, size; + int tst_value = 1; + int i; + int ret = 0; + + /* Determine this system's endianess */ + c1 = (unsigned char*)calloc(1, sizeof(int)); + memcpy(c1, &tst_value, sizeof(int)); + if(c1[0]==1) + endian = 0; /* little endian */ + else + endian = 1; /* big endian */ + + memset(&ld, 0, 12); + ld = (long double)l; + + size = sizeof(long double); + c2 = (unsigned char*)malloc(size); + memcpy(c2, &ld, size); + + /* Test if the last 2 bytes of mantissa are lost. Mainly for FreeBSD on Intel + * architecture(sleipnir) where it happens. */ + if(endian==0 && c2[0]==0 && c2[1]==0) /*little endian*/ + ret = 1; + + free(c1); + free(c2); +done: + exit(ret); + } + ], [hdf5_cv_ullong_to_ldouble_precision_works=yes], [hdf5_cv_ullong_to_ldouble_precision_works=no],)]) + +if test ${hdf5_cv_ullong_to_ldouble_precision_works} = "yes"; then + AC_DEFINE([ULLONG_TO_LDOUBLE_PRECISION_WORKS], [1], + [Define if your system can convert unsigned long long to long double with correct precision.]) + 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. diff --git a/src/H5config.h.in b/src/H5config.h.in index d7f27f9f16..aa30b7bf31 100644 --- a/src/H5config.h.in +++ b/src/H5config.h.in @@ -545,6 +545,10 @@ casts. */ #undef ULLONG_TO_FP_CAST_WORKS +/* Define if your system can convert unsigned long long to long double with + correct precision. */ +#undef ULLONG_TO_LDOUBLE_PRECISION_WORKS + /* Define if the HDF5 v1.6 compatibility functions are to be compiled in */ #undef WANT_H5_V1_6_COMPAT diff --git a/test/dtypes.c b/test/dtypes.c index dd941930ae..c6d6a571a0 100644 --- a/test/dtypes.c +++ b/test/dtypes.c @@ -5849,6 +5849,21 @@ test_conv_int_float(const char *name, hid_t src, hid_t dst) } #endif /*end H5_FP_TO_ULLONG_BOTTOM_BIT_WORKS*/ +/* For GNU compilers on FreeBSD(sleipnir), during conversion from 'unsigned long long' + * to 'long double', the last 2 bytes of mantissa are lost. But this loss seems + * acceptable. We allow it go through instead of fail it. + */ +#ifndef H5_ULLONG_TO_LDOUBLE_PRECISION_WORKS + if(src_type==INT_ULLONG && dst_type==FLT_LDOUBLE) { + long double tmp_s, tmp_h; + HDmemcpy(&tmp_s,&buf[j*dst_size],sizeof(long double)); + HDmemcpy(&tmp_h,&hw[0],sizeof(long double)); + /*Don't compare the last 2 bytes of mantissa*/ + if(!HDmemcmp(&tmp_s+2, &tmp_h+2, sizeof(long double)-2)) + continue; /*no error*/ + } +#endif /*end H5_ULLONG_TO_LDOUBLE_PRECISION_WORKS*/ + /* Print errors */ if (0==fails_this_test++) H5_FAILED();