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>