diff options
Diffstat (limited to 'src/util.c')
-rw-r--r-- | src/util.c | 49 |
1 files changed, 34 insertions, 15 deletions
diff --git a/src/util.c b/src/util.c index 45591d9f2..ae1f89b65 100644 --- a/src/util.c +++ b/src/util.c @@ -552,6 +552,36 @@ int string2d(const char *s, size_t slen, double *dp) { return 1; } +/* Returns 1 if the double value can safely be represented in long long without + * precision loss, in which case the corresponding long long is stored in the out variable. */ +int double2ll(double d, long long *out) { +#if (DBL_MANT_DIG >= 52) && (DBL_MANT_DIG <= 63) && (LLONG_MAX == 0x7fffffffffffffffLL) + /* Check if the float is in a safe range to be casted into a + * long long. We are assuming that long long is 64 bit here. + * Also we are assuming that there are no implementations around where + * double has precision < 52 bit. + * + * Under this assumptions we test if a double is inside a range + * where casting to long long is safe. Then using two castings we + * make sure the decimal part is zero. If all this is true we can use + * integer without precision loss. + * + * Note that numbers above 2^52 and below 2^63 use all the fraction bits as real part, + * and the exponent bits are positive, which means the "decimal" part must be 0. + * i.e. all double values in that range are representable as a long without precision loss, + * but not all long values in that range can be represented as a double. + * we only care about the first part here. */ + if (d < -LLONG_MAX/2 || d > LLONG_MAX/2) + return 0; + long long ll = d; + if (ll == d) { + *out = ll; + return 1; + } +#endif + return 0; +} + /* Convert a double to a string representation. Returns the number of bytes * required. The representation should always be parsable by strtod(3). * This function does not support human-friendly formatting like ld2string @@ -572,22 +602,11 @@ int d2string(char *buf, size_t len, double value) { else len = snprintf(buf,len,"0"); } else { -#if (DBL_MANT_DIG >= 52) && (LLONG_MAX == 0x7fffffffffffffffLL) - /* Check if the float is in a safe range to be casted into a - * long long. We are assuming that long long is 64 bit here. - * Also we are assuming that there are no implementations around where - * double has precision < 52 bit. - * - * Under this assumptions we test if a double is inside an interval - * where casting to long long is safe. Then using two castings we - * make sure the decimal part is zero. If all this is true we use - * integer printing function that is much faster. */ - double min = -4503599627370495; /* (2^52)-1 */ - double max = 4503599627370496; /* -(2^52) */ - if (value > min && value < max && value == ((double)((long long)value))) - len = ll2string(buf,len,(long long)value); + long long lvalue; + /* Integer printing function is much faster, check if we can safely use it. */ + if (double2ll(value, &lvalue)) + len = ll2string(buf,len,lvalue); else -#endif len = snprintf(buf,len,"%.17g",value); } |