Files
mercury/runtime/mercury_float.c
Ian MacLarty 067c703c56 Fix a bug in MR_sprintf_float where it would convert 1.8e-10
runtime/mercury_float.c:
    Fix a bug in MR_sprintf_float where it would convert 1.8e-10
    into "1.80000000000000e-1" instead of "1.8e-10".

    The problem was that it was stripping the zeros off the end of the string
    produced by sprintf without considering the case where the output
    was in scientific notation.

    The fix is to remove the `#' from the sprintf format string and then to
    append ".0" to the string if there is no "." or "e".

tests/general/float_roundtrip.m:
tests/general/float_roundtrip.exp:
    Add regression test.

tests/hard_coded/deep_copy.exp:
tests/hard_coded/expand.exp:
tests/hard_coded/float_reg.exp:
tests/hard_coded/write.exp:
tests/hard_coded/write_binary.exp:
tests/hard_coded/write_reg1.exp:
tests/hard_coded/write_xml.exp:
    Update the expected output of these tests as trailing zeros are now
    also removed from the base part of scientific notation float strings.
2010-07-16 07:19:12 +00:00

130 lines
3.1 KiB
C

/*
** vim: ts=4 sw=4 expandtab
*/
/*
** Copyright (C) 1997, 2000, 2002, 2006-2007 The University of Melbourne.
** This file may only be copied under the terms of the GNU Library General
** Public License - see the file COPYING.LIB in the Mercury distribution.
*/
#include "mercury_imp.h"
#include <math.h>
/*
** The function `MR_hash_float()' is used by the library predicate
** `float__hash' and also for hashing floats for `pragma fact_table' indexing.
** It computes a non-negative MR_Integer hash value for a MR_Float.
** The exact hash function used depend on the relative sizes of MR_Float and
** MR_Integer.
*/
union MR_Float_Integer {
MR_Float f;
MR_Integer i;
MR_Integer j[(sizeof(MR_Float)/sizeof(MR_Integer) > 0
? sizeof(MR_Float)/sizeof(MR_Integer) : 1)];
char c[sizeof(MR_Float)/sizeof(char)];
};
MR_Integer
MR_hash_float(MR_Float f)
{
union MR_Float_Integer fi;
size_t i;
MR_Integer h = 0;
fi.i = 0;
fi.f = f;
if (sizeof(MR_Float) <= sizeof(MR_Integer)) {
h = fi.i;
} else if (sizeof(MR_Float) % sizeof(MR_Integer) == 0) {
for (i = 0; i < sizeof(MR_Float)/sizeof(MR_Integer); i++) {
h ^= fi.j[i];
}
} else {
for (i = 0; i < sizeof(MR_Float)/sizeof(char); i++) {
h ^= (h << 5);
h ^= fi.c[i];
}
}
return (h >= 0 ? h : -h);
}
/*
** MR_sprintf_float(buf, f)
**
** fills buff with the string representation of the float, f, such that
** the string representation has enough precision to represent the
** float, f.
**
** Note that buf must have size at least ML_SPRINTF_FLOAT_BUF_SIZE.
*/
void
MR_sprintf_float(char *buf, MR_Float f)
{
MR_Float round_trip = 0.0;
int i = MR_FLT_MIN_PRECISION;
int n;
/*
** Print the float at increasing precisions until the float
** is round-trippable.
*/
do {
sprintf(buf, "%.*g", i, f);
if (i >= MR_FLT_MAX_PRECISION) {
/*
** This should be sufficient precision to round-trip any value.
** Don't bother checking whether it can actually be round-tripped,
** since if it can't, this is a bug in the C implementation.
*/
break;
}
sscanf(buf, MR_FLT_FMT, &round_trip);
i++;
} while (round_trip != f);
/*
** Append ".0" if there is no "e" or "." in the string.
*/
while (1) {
if (*buf == 'e' || *buf == '.') {
return;
}
if (*buf == '\0') {
/* We only get here if there is no '.' or 'e' in the string. */
strcpy(buf, ".0");
return;
}
buf++;
}
return;
}
MR_bool
MR_is_nan(MR_Float Flt)
{
#if defined(MR_USE_SINGLE_PREC_FLOAT) && defined(MR_HAVE_ISNANF)
return isnanf(Flt);
#elif defined(MR_HAVE_ISNAN)
return isnan(Flt);
#else
return (Flt != Flt);
#endif
}
MR_bool
MR_is_inf(MR_Float Flt)
{
#if defined(MR_USE_SINGLE_PREC_FLOAT) && defined(MR_HAVE_ISINFF)
return isinff(Flt);
#elif defined(MR_HAVE_ISINF)
return isinf(Flt);
#else
return (Flt == Flt / 2.0 && Flt != 0.0);
#endif
}