diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2019-05-19 20:34:20 +0100 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2019-05-22 14:39:08 +0100 |
commit | 93f200d61e8d7642167083e735895708f9c377ba (patch) | |
tree | 7da405bc88eb67079571ccaf092c9ebd77ffa3de | |
parent | 991ba4e2e3b909b8f971855eece588ef674c6b48 (diff) | |
download | rspamd-93f200d61e8d7642167083e735895708f9c377ba.tar.gz rspamd-93f200d61e8d7642167083e735895708f9c377ba.zip |
[Fix] Further fixes to printing of the FP numbers
-rw-r--r-- | contrib/fpconv/fpconv.c | 226 | ||||
-rw-r--r-- | contrib/fpconv/fpconv.h | 4 | ||||
-rw-r--r-- | src/libutil/printf.c | 75 |
3 files changed, 189 insertions, 116 deletions
diff --git a/contrib/fpconv/fpconv.c b/contrib/fpconv/fpconv.c index 71818828f..f8b601a55 100644 --- a/contrib/fpconv/fpconv.c +++ b/contrib/fpconv/fpconv.c @@ -1,5 +1,6 @@ #include <stdbool.h> #include <string.h> +#include <sys/param.h> #include "fpconv.h" #include "powers.h" @@ -199,54 +200,32 @@ static int grisu2 (double d, char *digits, int *K) { return generate_digits (&w, &upper, &lower, digits, K); } -static int emit_digits (char *digits, int ndigits, char *dest, int K, bool neg, - bool scientific) { - int exp = absv(K + ndigits - 1); - - /* write plain integer */ - if (K >= 0 && (exp < (ndigits + 7))) { - memcpy(dest, digits, ndigits); - memset(dest + ndigits, '0', K); - - return ndigits + K; - } - - /* write decimal w/o scientific notation */ - if (!scientific || (K < 0 && (K > -7 || exp < 4))) { - int offset = ndigits - absv(K); - /* fp < 1.0 -> write leading zero */ - if (offset <= 0) { - offset = -offset; - dest[0] = '0'; - dest[1] = '.'; - - /* We have up to 21 characters in output available */ - if (offset + ndigits <= 21) { - memset(dest + 2, '0', offset); - memcpy(dest + offset + 2, digits, ndigits); +static inline int emit_integer (char *digits, int ndigits, + char *dest, int K, bool neg, + unsigned precision) +{ + char *d = dest; - return ndigits + 2 + offset; - } - else { - goto scientific_fallback; - } + memcpy (d, digits, ndigits); + d += ndigits; + memset (d, '0', K); + d += K; - /* fp > 1.0 */ - } - else { - /* Overflow check */ - if (ndigits <= 23) { - memcpy(dest, digits, offset); - dest[offset] = '.'; - memcpy(dest + offset + 1, digits + offset, ndigits - offset); - return ndigits + 1; - } + precision = MIN(precision, FPCONV_BUFLEN - (ndigits + K + 1)); - goto scientific_fallback; - } + if (precision) { + *d++ = '.'; + memset (d, '0', precision); + d += precision; } - scientific_fallback: + return d - dest; +} + +static inline int emit_scientific_digits (char *digits, int ndigits, + char *dest, int K, bool neg, + unsigned precision, int exp) +{ /* write decimal w/ scientific notation */ ndigits = minv(ndigits, 18 - neg); @@ -286,19 +265,153 @@ static int emit_digits (char *digits, int ndigits, char *dest, int K, bool neg, return idx; } -static int filter_special (double fp, char *dest) { +static inline int emit_fixed_digits (char *digits, int ndigits, + char *dest, int K, bool neg, + unsigned precision, int exp) +{ + int offset = ndigits - absv(K), to_print; + /* fp < 1.0 -> write leading zero */ + if (K < 0) { + if (offset <= 0) { + if (precision) { + if (-offset >= precision) { + /* Just print 0.[0]{precision} */ + dest[0] = '0'; + dest[1] = '.'; + memset(dest + 2, '0', precision); + + return precision + 2; + } + + to_print = MAX(ndigits - offset, precision); + } + else { + to_print = ndigits - offset; + } + + if (to_print <= FPCONV_BUFLEN - 3) { + offset = -offset; + dest[0] = '0'; + dest[1] = '.'; + memset(dest + 2, '0', offset); + + if (precision) { + /* The case where offset > precision is covered previously */ + precision -= offset; + + if (precision <= ndigits) { + /* Truncate or leave as is */ + memcpy(dest + offset + 2, digits, precision); + + return precision + 2 + offset; + } + else { + /* Expand */ + memcpy(dest + offset + 2, digits, ndigits); + precision -= ndigits; + memset(dest + offset + 2 + ndigits, '0', precision); + + return ndigits + 2 + offset + precision; + } + } + else { + memcpy(dest + offset + 2, digits, ndigits); + } + + return ndigits + 2 + offset; + } + else { + return emit_scientific_digits (digits, ndigits, dest, K, neg, precision, exp); + } + } + else { + /* + * fp > 1.0, if offset > 0 then we have less digits than + * fp exponent, so we need to switch to scientific notation to + * display number at least more or less precisely + */ + if (offset > 0 && ndigits <= FPCONV_BUFLEN - 3) { + char *d = dest; + memcpy(d, digits, offset); + d += offset; + *d++ = '.'; + + ndigits -= offset; + + if (precision) { + if (ndigits >= precision) { + /* Truncate or leave as is */ + memcpy(d, digits + offset, precision); + d += precision; + } + else { + /* Expand */ + memcpy(d, digits + offset, ndigits); + precision -= ndigits; + d += ndigits; + + /* Check if we have enough bufspace */ + if ((d - dest) + precision <= FPCONV_BUFLEN) { + memset (d, '0', precision); + d += precision; + } + else { + memset (d, '0', FPCONV_BUFLEN - (d - dest)); + d += FPCONV_BUFLEN - (d - dest); + } + } + } + else { + memcpy(d, digits + offset, ndigits); + d += ndigits; + } + + return d - dest; + } + } + } + + return emit_scientific_digits (digits, ndigits, dest, K, neg, precision, exp); +} + +static int emit_digits (char *digits, int ndigits, char *dest, int K, bool neg, + unsigned precision, bool scientific) +{ + int exp = absv(K + ndigits - 1); + + /* write plain integer */ + if (K >= 0 && (exp < (ndigits + 7))) { + return emit_integer (digits, ndigits, dest, K, neg, precision); + } + + /* write decimal w/o scientific notation */ + if (!scientific || (K < 0 && (K > -7 || exp < 4))) { + return emit_fixed_digits (digits, ndigits, dest, K, neg, precision, exp); + } + + return emit_scientific_digits (digits, ndigits, dest, K, neg, precision, exp); +} + +static int filter_special (double fp, char *dest, unsigned precision) +{ int nchars = 3; + char *d = dest; if (fp == 0.0) { if (get_dbits (fp) & signmask) { - dest[0] = '-'; - dest[1] = '0'; - return 2; + *d++ = '-'; + *d++ = '0'; } else { - dest[0] = '0'; - return 1; + *d++ = '0'; } + + if (precision) { + *d ++ = '.'; + memset (d, '0', precision); + } + + return d - dest + precision; } uint64_t bits = get_dbits (fp); @@ -332,17 +445,23 @@ static int filter_special (double fp, char *dest) { return nchars; } -int fpconv_dtoa (double d, char dest[24], bool scientific) { +int +fpconv_dtoa (double d, char dest[FPCONV_BUFLEN], + unsigned precision, bool scientific) +{ char digits[18]; int str_len = 0; bool neg = false; + if (precision > FPCONV_BUFLEN - 5) { + precision = FPCONV_BUFLEN - 5; + } - int spec = filter_special (d, dest + str_len); + int spec = filter_special (d, dest, precision); if (spec) { - return str_len + spec; + return spec; } if (get_dbits (d) & signmask) { @@ -354,7 +473,8 @@ int fpconv_dtoa (double d, char dest[24], bool scientific) { int K = 0; int ndigits = grisu2 (d, digits, &K); - str_len += emit_digits (digits, ndigits, dest + str_len, K, neg, scientific); + str_len += emit_digits (digits, ndigits, dest + str_len, K, neg, precision, + scientific); return str_len; } diff --git a/contrib/fpconv/fpconv.h b/contrib/fpconv/fpconv.h index 0bc270b31..8c07c1368 100644 --- a/contrib/fpconv/fpconv.h +++ b/contrib/fpconv/fpconv.h @@ -1,6 +1,7 @@ #ifndef FPCONV_H #define FPCONV_H +#define FPCONV_BUFLEN 32 /* Fast and accurate double to string conversion based on Florian Loitsch's * Grisu-algorithm[1]. * @@ -26,7 +27,8 @@ * */ -int fpconv_dtoa(double fp, char dest[32], bool scientific); +int fpconv_dtoa(double fp, char dest[FPCONV_BUFLEN], unsigned precision, + bool scientific); #endif diff --git a/src/libutil/printf.c b/src/libutil/printf.c index 6739fe142..9db2bfe7d 100644 --- a/src/libutil/printf.c +++ b/src/libutil/printf.c @@ -591,7 +591,8 @@ rspamd_vprintf_common (rspamd_printf_append_func func, const gchar *fmt, va_list args) { - gchar zero, numbuf[G_ASCII_DTOSTR_BUF_SIZE], dtoabuf[32], *p, *last, c; + gchar zero, numbuf[G_ASCII_DTOSTR_BUF_SIZE], dtoabuf[32], *p, *last; + guchar c; const gchar *buf_start = fmt, *fmt_start = NULL; gint d; gdouble f; @@ -839,9 +840,9 @@ rspamd_vprintf_common (rspamd_printf_append_func func, } while (slen) { - hexbuf[0] = hex == 2 ? _HEX[(*p >> 4) & 0xf] : - _hex[(*p >> 4) & 0xf]; - hexbuf[1] = hex == 2 ? _HEX[*p & 0xf] : _hex[*p & 0xf]; + hexbuf[0] = hex == 2 ? _HEX[(*p >> 4u) & 0xfu] : + _hex[(*p >> 4u) & 0xfu]; + hexbuf[1] = hex == 2 ? _HEX[*p & 0xfu] : _hex[*p & 0xfu]; RSPAMD_PRINTF_APPEND_BUF (hexbuf, 2); p++; slen--; @@ -948,32 +949,7 @@ rspamd_vprintf_common (rspamd_printf_append_func func, case 'f': f = (gdouble) va_arg (args, double); - slen = fpconv_dtoa (f, dtoabuf, false); - - if (frac_width != 0) { - const gchar *dot_pos = memchr (dtoabuf, '.', slen); - - if (dot_pos) { - if (frac_width < (slen - ((dot_pos - dtoabuf) + 1))) { - /* Truncate */ - slen = (dot_pos - dtoabuf) + 1 + /* xxx. */ - frac_width; /* .yyy */ - } - else if (frac_width + dot_pos + 1 < dtoabuf + sizeof (dtoabuf)) { - /* Expand */ - frac_width -= slen - ((dot_pos - dtoabuf) + 1); - memset (dtoabuf + slen, '0', frac_width); - slen += frac_width; - } - } - else { - /* Expand */ - frac_width = MIN (frac_width, sizeof (dtoabuf) - slen - 1); - dtoabuf[slen ++] = '.'; - memset (dtoabuf + slen, '0', frac_width); - slen += frac_width; - } - } + slen = fpconv_dtoa (f, dtoabuf, frac_width, false); RSPAMD_PRINTF_APPEND (dtoabuf, slen); @@ -981,39 +957,14 @@ rspamd_vprintf_common (rspamd_printf_append_func func, case 'g': f = (gdouble) va_arg (args, double); - slen = fpconv_dtoa (f, dtoabuf, true); + slen = fpconv_dtoa (f, dtoabuf, 0, true); RSPAMD_PRINTF_APPEND (dtoabuf, slen); continue; case 'F': f = (gdouble) va_arg (args, long double); - slen = fpconv_dtoa (f, dtoabuf, false); - - if (frac_width != 0) { - const gchar *dot_pos = memchr (dtoabuf, '.', slen); - - if (dot_pos) { - if (frac_width < (slen - ((dot_pos - dtoabuf) + 1))) { - /* Truncate */ - slen = (dot_pos - dtoabuf) + 1 + /* xxx. */ - frac_width; /* .yyy */ - } - else if (frac_width + dot_pos + 1 < dtoabuf + sizeof (dtoabuf)) { - /* Expand */ - frac_width -= slen - ((dot_pos - dtoabuf) + 1); - memset (dtoabuf + slen, '0', frac_width); - slen += frac_width; - } - } - else { - /* Expand */ - frac_width = MIN (frac_width, sizeof (dtoabuf) - slen - 1); - dtoabuf[slen ++] = '.'; - memset (dtoabuf + slen, '0', frac_width); - slen += frac_width; - } - } + slen = fpconv_dtoa (f, dtoabuf, frac_width, false); RSPAMD_PRINTF_APPEND (dtoabuf, slen); @@ -1021,7 +972,7 @@ rspamd_vprintf_common (rspamd_printf_append_func func, case 'G': f = (gdouble) va_arg (args, long double); - slen = fpconv_dtoa (f, dtoabuf, true); + slen = fpconv_dtoa (f, dtoabuf, 0, true); RSPAMD_PRINTF_APPEND (dtoabuf, slen); continue; @@ -1036,12 +987,12 @@ rspamd_vprintf_common (rspamd_printf_append_func func, case 'c': c = va_arg (args, gint); - c &= 0xff; + c &= 0xffu; if (G_UNLIKELY (hex)) { gchar hexbuf[2]; - hexbuf[0] = hex == 2 ? _HEX[(c >> 4) & 0xf] : - _hex[(c >> 4) & 0xf]; - hexbuf[1] = hex == 2 ? _HEX[c & 0xf] : _hex[c & 0xf]; + hexbuf[0] = hex == 2 ? _HEX[(c >> 4u) & 0xfu] : + _hex[(c >> 4u) & 0xfu]; + hexbuf[1] = hex == 2 ? _HEX[c & 0xfu] : _hex[c & 0xfu]; RSPAMD_PRINTF_APPEND (hexbuf, 2); } |