Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 12 Feb 2002 15:10:23 +0200
From:      Alexey Zelkin <phantom@freebsd.org>
To:        hackers@freebsd.org, fenner@freebsd.org, standards@freebsd.org
Subject:   CFR: printf grouping support for floats (%'f)
Message-ID:  <20020212151023.A8369@gate.sim.ionidea.com>

next in thread | raw e-mail | index | archive | help
hi,

This patch fixes *printf() family routines to correctly handle
grouping for both decimals and floats. Current version of printf()
supports grouping for decimals only.

Yes, I know it looks like more hackish way, so other opinions are
welcome!

Since printf() is widely used and quite important function I'd like
to get as much comments as possible (optimizations, simplifications,
others)

Index: vfprintf.c
===================================================================
RCS file: /home/cvs/freebsd/src/lib/libc/stdio/vfprintf.c,v
retrieving revision 1.36
diff -u -r1.36 vfprintf.c
--- vfprintf.c	17 Dec 2001 15:11:29 -0000	1.36
+++ vfprintf.c	12 Feb 2002 13:02:00 -0000
@@ -114,12 +114,11 @@
 
 static int	__sprint __P((FILE *, struct __suio *));
 static int	__sbprintf __P((FILE *, const char *, va_list)) __printflike(2, 0);
-static char	*__ujtoa __P((uintmax_t, char *, int, int, char *, int,
-		     char, const char *));
-static char	*__ultoa __P((u_long, char *, int, int, char *, int,
-		     char, const char *));
+static char	*__ujtoa __P((uintmax_t, char *, int, int, char *));
+static char	*__ultoa __P((u_long, char *, int, int, char *));
 static void	__find_arguments __P((const char *, va_list, union arg **));
 static void	__grow_type_table __P((int, enum typeid **, int *));
+static int	__do_grouping __P((char *, char **, int, int));
 
 /*
  * Flush out all the vectors defined by the given uio,
@@ -186,12 +185,10 @@
  * use the given digits.
  */
 static char *
-__ultoa(u_long val, char *endp, int base, int octzero, char *xdigs,
-	int needgrp, char thousep, const char *grp)
+__ultoa(u_long val, char *endp, int base, int octzero, char *xdigs)
 {
 	register char *cp = endp;
 	register long sval;
-	int ndig;
 
 	/*
 	 * Handle the three cases separately, in the hope of getting
@@ -203,7 +200,6 @@
 			*--cp = to_char(val);
 			return (cp);
 		}
-		ndig = 0;
 		/*
 		 * On many machines, unsigned arithmetic is harder than
 		 * signed arithmetic, so we do at most one unsigned mod and
@@ -212,29 +208,11 @@
 		 */
 		if (val > LONG_MAX) {
 			*--cp = to_char(val % 10);
-			ndig++;
 			sval = val / 10;
 		} else
 			sval = val;
 		do {
 			*--cp = to_char(sval % 10);
-			ndig++;
-			/*
-			 * If (*grp == CHAR_MAX) then no more grouping
-			 * should be performed.
-			 */
-			if (needgrp && ndig == *grp && *grp != CHAR_MAX
-					&& sval > 9) {
-				*--cp = thousep;
-				ndig = 0;
-				/*
-				 * If (*(grp+1) == '\0') then we have to
-				 * use *grp character (last grouping rule)
-				 * for all next cases
-				 */
-				if (*(grp+1) != '\0')
-					grp++;
-			}
 			sval /= 10;
 		} while (sval != 0);
 		break;
@@ -263,50 +241,28 @@
 
 /* Identical to __ultoa, but for intmax_t. */
 static char *
-__ujtoa(uintmax_t val, char *endp, int base, int octzero, char *xdigs, 
-	int needgrp, char thousep, const char *grp)
+__ujtoa(uintmax_t val, char *endp, int base, int octzero, char *xdigs)
 {
 	char *cp = endp;
 	intmax_t sval;
-	int ndig;
 
 	/* quick test for small values; __ultoa is typically much faster */
 	/* (perhaps instead we should run until small, then call __ultoa?) */
 	if (val <= ULONG_MAX)
-		return (__ultoa((u_long)val, endp, base, octzero, xdigs,
-		    needgrp, thousep, grp));
+		return (__ultoa((u_long)val, endp, base, octzero, xdigs));
 	switch (base) {
 	case 10:
 		if (val < 10) {
 			*--cp = to_char(val % 10);
 			return (cp);
 		}
-		ndig = 0;
 		if (val > INTMAX_MAX) {
 			*--cp = to_char(val % 10);
-			ndig++;
 			sval = val / 10;
 		} else
 			sval = val;
 		do {
 			*--cp = to_char(sval % 10);
-			ndig++;
-			/*
-			 * If (*grp == CHAR_MAX) then no more grouping
-			 * should be performed.
-			 */
-			if (needgrp && *grp != CHAR_MAX && ndig == *grp
-					&& sval > 9) {
-				*--cp = thousep;
-				ndig = 0;
-				/*
-				 * If (*(grp+1) == '\0') then we have to
-				 * use *grp character (last grouping rule)
-				 * for all next cases
-				 */
-				if (*(grp+1) != '\0')
-					grp++;
-			}
 			sval /= 10;
 		} while (sval != 0);
 		break;
@@ -400,8 +356,6 @@
 	int width;		/* width from format (%8d), or 0 */
 	int prec;		/* precision from format (%.3d), or -1 */
 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
-	char thousands_sep;	/* locale specific thousands separator */
-	const char *grouping;	/* locale specific numeric grouping rules */
 #ifdef FLOATING_POINT
 	char *decimal_point;	/* locale specific decimal point */
 	char softsign;		/* temporary negative sign for floats */
@@ -532,8 +486,6 @@
         }
 
 
-	thousands_sep = '\0';
-	grouping = NULL;
 #ifdef FLOATING_POINT
 	dtoaresult = NULL;
 	decimal_point = localeconv()->decimal_point;
@@ -614,8 +566,6 @@
 			goto rflag;
 		case '\'':
 			flags |= GROUPING;
-			thousands_sep = *(localeconv()->thousands_sep);
-			grouping = localeconv()->grouping;
 			goto rflag;
 		case '.':
 			if ((ch = *fmt++) == '*') {
@@ -750,6 +700,7 @@
 				else
 					cp = "inf";
 				size = 3;
+				flags &= ~GROUPING;
 				break;
 			}
 			if (isnan(_double)) {
@@ -758,6 +709,7 @@
 				else
 					cp = "nan";
 				size = 3;
+				flags &= ~GROUPING;
 				break;
 			}
 			flags |= FPT;
@@ -912,15 +864,11 @@
 			if (flags & INTMAX_SIZE) {
 				if (ujval != 0 || prec != 0)
 					cp = __ujtoa(ujval, cp, base,
-					    flags & ALT, xdigs,
-					    flags & GROUPING, thousands_sep,
-					    grouping);
+					    flags & ALT, xdigs);
 			} else {
 				if (ulval != 0 || prec != 0)
 					cp = __ultoa(ulval, cp, base,
-					    flags & ALT, xdigs,
-					    flags & GROUPING, thousands_sep,
-					    grouping);
+					    flags & ALT, xdigs);
 			}
 			size = buf + BUF - cp;
 			break;
@@ -981,9 +929,29 @@
 		/* leading zeroes from decimal precision */
 		PAD(dprec - size, zeroes);
 
+#define DOGRP0(PTR, RET, SIZE, SAFETY) {		\
+	RET = __do_grouping(buf, &PTR, SIZE, SAFETY);	\
+}
+#define DOGRP1(PTR, SIZE) {				\
+	if (flags & GROUPING) {				\
+		DOGRP0(PTR, n2, SIZE, 0);		\
+		SIZE += n2;				\
+		realsz += n2;				\
+	}						\
+}
+#define DOGRP2(PTR, SIZE1, SIZE2, SAFETY) {		\
+	if (flags & GROUPING) {				\
+		DOGRP0(PTR, n2, SIZE1, SAFETY);		\
+		SIZE1 += n2;				\
+		SIZE2 += n2;				\
+		realsz += n2;				\
+	}						\
+}
+
 		/* the string or number proper */
 #ifdef FLOATING_POINT
 		if ((flags & FPT) == 0) {
+			DOGRP1(cp, size);
 			PRINT(cp, size);
 		} else {	/* glue together f_p fragments */
 			if (ch >= 'f') {	/* 'f' or 'g' */
@@ -1000,11 +968,13 @@
 					PAD(-expt, zeroes);
 					PRINT(cp, ndig);
 				} else if (expt >= ndig) {
+					DOGRP1(cp, ndig);
 					PRINT(cp, ndig);
 					PAD(expt - ndig, zeroes);
 					if (flags & ALT)
 						PRINT(decimal_point, 1);
 				} else {
+					DOGRP2(cp, expt, ndig, ndig-expt);
 					PRINT(cp, expt);
 					cp += expt;
 					PRINT(decimal_point, 1);
@@ -1026,6 +996,7 @@
 			}
 		}
 #else
+		DOGRP1(cp, size);
 		PRINT(cp, size);
 #endif
 		/* left-adjusting padding (always blank) */
@@ -1387,6 +1358,67 @@
 
 	*typetable = newtable;
 	*tablesize = newsize;
+}
+
+/*-
+ * Reformat buffer to include grouping characters
+ *
+ * params:
+ *   buf -- pointer to begining of the buffer
+ *   cp -- address of pointer to actual adta 
+ *   size -- amount of characters to group
+ *   safety -- amount of characters after (*cp+size) to preserve
+ *             (post-decimal part of FPT number)
+ */
+static int
+__do_grouping(char *buf, char **cp, int size, int safety) {
+
+	char thousands_sep;
+	char *grouping;
+	char *ptr, *tp;
+	int ndig, proceed, result, n;
+
+	/* XXX: return value of __dtoa() *possibly* *may* overflow size
+	 * of internal buffer.  Check this situation later.
+         */
+
+	grouping = localeconv()->grouping;
+	if (*grouping == CHAR_MAX)
+		return (0);
+
+	thousands_sep = *(localeconv()->thousands_sep);
+	if (thousands_sep == '\0')
+		return (0);
+
+	result = 0;
+
+	/* make sure that data is at the begin of the buffer */
+	if (buf != *cp) {
+		memmove(buf, *cp, size+safety);
+		*cp = buf;
+	}
+
+	ndig = 0;
+	proceed = 0;
+	ptr = (*cp + size);
+	while (proceed < size) {
+		if (*grouping <= ndig) {
+			memmove(ptr+1, ptr, proceed+result+safety);
+			*ptr = thousands_sep;
+			result++;
+			if (*(grouping+1) != '\0')
+				grouping++;
+			if (*grouping == CHAR_MAX)	/* no more grouping */
+				break;
+			ndig = 0;
+		} else {
+			ndig++;
+			proceed++;
+			ptr--;
+		}
+	}
+
+	return (result);
 }
 
 

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020212151023.A8369>