commit 8fe256d: [Fix] Further fixes to printing of the FP numbers

Vsevolod Stakhov vsevolod at highsecure.ru
Sun May 19 19:35:06 UTC 2019


Author: Vsevolod Stakhov
Date: 2019-05-19 20:34:20 +0100
URL: https://github.com/rspamd/rspamd/commit/8fe256d18f0d05614d724fc20fa74bddc91e0c1f

[Fix] Further fixes to printing of the FP numbers

---
 contrib/fpconv/fpconv.c | 226 ++++++++++++++++++++++++++++++++++++------------
 contrib/fpconv/fpconv.h |   4 +-
 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);
 				}


More information about the Commits mailing list