[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.
This commit is contained in:
Raymond Lu 2006-06-20 09:45:37 -05:00
parent 9db9e82cd1
commit 823fbd0e88
5 changed files with 193 additions and 9 deletions

100
configure vendored
View File

@ -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`"

View File

@ -3093,6 +3093,69 @@ else
AC_MSG_RESULT([no])
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.

View File

@ -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

View File

@ -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

View File

@ -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*/