From 313a9b41133ca631890de99f979ee01074d0c0be Mon Sep 17 00:00:00 2001 From: Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de> Date: Sat, 19 Apr 2008 17:39:23 +0200 Subject: [PATCH] Fixes/enhancements for INF/NAN handling in printf() This patch adds support for uppercase 'F' and 'G' printf format specifiers. It fixes handling of -INF values in __dtostr() too; previously, there was | unsigned int i; | if ((i=isinf(d))) return copystring(buf,maxlen,i>0?"inf":"-inf"); ~~~ which evaluated to true everytime. The copystring() function worked for 3-letter words only but not for '-inf'. The last argument of __dtostr() was changed from a boolean flag to a bitmask. Bit 0 encodes 'g' or 'f', and bit 1 lower-/uppercase. There should be probably added some macros for them; for now, these values are used directly. Please note that this might affect other applications (liblowfat?) too which are using __dtostr(). 'isinf(3)' is a builtin with gcc 4.3 and does not give a hint about the signess of the infinity anymore. Hence, this patch uses a more portable way where needed. --- include/stdlib.h | 6 ++++- lib/__dtostr.c | 18 +++++++++++----- lib/__v_printf.c | 59 +++++++++++++++++++++++++++++------------------------ test/printf.c | 44 +++++++++++++++++++++++++++++++++++---- 4 files changed, 88 insertions(+), 39 deletions(-) diff --git a/include/stdlib.h b/include/stdlib.h index d1e1569..34f3a7f 100644 --- a/include/stdlib.h +++ b/include/stdlib.h @@ -28,8 +28,12 @@ long double strtold(const char *nptr, char **endptr) __THROW; long int strtol(const char *nptr, char **endptr, int base) __THROW; unsigned long int strtoul(const char *nptr, char **endptr, int base) __THROW; +/* HACK: used flags in __dtostr + 0x01 ... 'g' + 0x02 ... uppercase + Define some constants somewhere... */ extern int __ltostr(char *s, unsigned int size, unsigned long i, unsigned int base, int UpCase) __THROW; -extern int __dtostr(double d,char *buf,unsigned int maxlen,unsigned int prec,unsigned int prec2,int g) __THROW; +extern int __dtostr(double d,char *buf,unsigned int maxlen,unsigned int prec,unsigned int prec2,int flags) __THROW; #if !defined(__STRICT_ANSI__) || __STDC_VERSION__ + 0 >= 199900L __extension__ long long int strtoll(const char *nptr, char **endptr, int base) __THROW; diff --git a/lib/__dtostr.c b/lib/__dtostr.c index 1d082e3..bc61200 100644 --- a/lib/__dtostr.c +++ b/lib/__dtostr.c @@ -5,13 +5,15 @@ static int copystring(char* buf,int maxlen, const char* s) { int i; - for (i=0; i<3&&i<maxlen; ++i) + for (i=0; i<maxlen; ++i) { buf[i]=s[i]; - if (i<maxlen) { buf[i]=0; ++i; } + if (!s[i]) + break; + } return i; } -int __dtostr(double d,char *buf,unsigned int maxlen,unsigned int prec,unsigned int prec2,int g) { +int __dtostr(double d,char *buf,unsigned int maxlen,unsigned int prec,unsigned int prec2,int flags) { #if 1 union { unsigned long long l; @@ -35,8 +37,12 @@ int __dtostr(double d,char *buf,unsigned int maxlen,unsigned int prec,unsigned i double tmp; char *oldbuf=buf; - if ((i=isinf(d))) return copystring(buf,maxlen,i>0?"inf":"-inf"); - if (isnan(d)) return copystring(buf,maxlen,"nan"); + if (isinf(d)) + return copystring(buf,maxlen, + (d<0)? + (flags&0x02?"-INF":"-inf"): + (flags&0x02?"INF":"inf")); + if (isnan(d)) return copystring(buf,maxlen,flags&0x02?"NAN":"nan"); e10=1+(long)(e*0.30102999566398119802); /* log10(2) */ /* Wir iterieren von Links bis wir bei 0 sind oder maxlen erreicht * ist. Wenn maxlen erreicht ist, machen wir das nochmal in @@ -126,7 +132,7 @@ int __dtostr(double d,char *buf,unsigned int maxlen,unsigned int prec,unsigned i if (prec2 || prec>(unsigned int)(buf-oldbuf)+1) { /* more digits wanted */ if (!maxlen) return 0; --maxlen; *buf='.'; ++buf; - if (g) { + if ((flags & 0x01)) { if (prec2) prec=prec2; prec-=buf-oldbuf-1; } else { diff --git a/lib/__v_printf.c b/lib/__v_printf.c index 36202f5..964c005 100644 --- a/lib/__v_printf.c +++ b/lib/__v_printf.c @@ -4,6 +4,7 @@ #include <stdlib.h> #include <string.h> #include <errno.h> +#include <math.h> #include "dietstdio.h" #include "dietwarning.h" @@ -346,45 +347,49 @@ num_printf: #ifdef WANT_FLOATING_POINT_IN_PRINTF /* print a floating point value */ case 'f': + case 'F': case 'g': + case 'G': { - int g=(ch=='g'); + int flags=(((ch&0x5f)=='G') ? 0x01 : 0x00) | ((ch&0x20) ? 0x00 : 0x02); double d=va_arg(arg_ptr,double); s=buf+1; if (width==0) width=1; if (!flag_dot) preci=6; if (flag_sign || d < +0.0) flag_in_sign=1; - sz=__dtostr(d,s,sizeof(buf)-1,width,preci,g); - - if (flag_dot) { - char *tmp; - if ((tmp=strchr(s,'.'))) { - if (preci || flag_hash) ++tmp; - while (preci>0 && *++tmp) --preci; - *tmp=0; - } else if (flag_hash) { - s[sz]='.'; - s[++sz]='\0'; + sz=__dtostr(d,s,sizeof(buf)-1,width,preci,flags); + + if (!isnan(d) && !isinf(d)) { /* skip NaN + INF values */ + if (flag_dot) { + char *tmp; + if ((tmp=strchr(s,'.'))) { + if (preci || flag_hash) ++tmp; + while (preci>0 && *++tmp) --preci; + *tmp=0; + } else if (flag_hash) { + s[sz]='.'; + s[++sz]='\0'; + } } - } - if (g) { - char *tmp,*tmp1; /* boy, is _this_ ugly! */ - if ((tmp=strchr(s,'.'))) { - tmp1=strchr(tmp,'e'); - while (*tmp) ++tmp; - if (tmp1) tmp=tmp1; - while (*--tmp=='0') ; - if (*tmp!='.') ++tmp; - *tmp=0; - if (tmp1) strcpy(tmp,tmp1); + if ((flags&0x01)) { + char *tmp,*tmp1; /* boy, is _this_ ugly! */ + if ((tmp=strchr(s,'.'))) { + tmp1=strchr(tmp,'e'); + while (*tmp) ++tmp; + if (tmp1) tmp=tmp1; + while (*--tmp=='0') ; + if (*tmp!='.') ++tmp; + *tmp=0; + if (tmp1) strcpy(tmp,tmp1); + } } - } - if ((flag_sign || flag_space) && d>=0) { - *(--s)=(flag_sign)?'+':' '; - ++sz; + if ((flag_sign || flag_space) && d>=0) { + *(--s)=(flag_sign)?'+':' '; + ++sz; + } } sz=strlen(s); diff --git a/test/printf.c b/test/printf.c index 719461a..ef6050d 100644 --- a/test/printf.c +++ b/test/printf.c @@ -2,11 +2,26 @@ #include <string.h> #include <stdlib.h> #include <assert.h> +#include <math.h> +#include <float.h> #include <sys/param.h> #include <locale.h> #define ALGN 5 +#ifndef INFINITY +# if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) +# define INFINITY (__builtin_inf()) +# endif +#endif + +#ifndef NAN +# if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) +# define NAN (__builtin_nan("")) +# endif +#endif + + // https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=112986 #if 0 #undef assert @@ -60,7 +75,7 @@ TEST_SNPRINTF(EXP, 0, __VA_ARGS__); \ TEST_SNPRINTF(EXP, sizeof(EXP)+ALGN, __VA_ARGS__); \ TEST_SNPRINTF_NULL(EXP, __VA_ARGS__) - + int main() { @@ -101,7 +116,7 @@ int main() TEST("42.23", "%5.2f", 42.23); TEST("42.23", "%5.4g", 42.23); TEST(" 42.2", "%5.3g", 42.23); - + TEST(" 1", "%*i", 4, 1); TEST(" 1", "%4i", 1); TEST("1 ", "%-4i", 1); @@ -131,13 +146,32 @@ int main() TEST("-01234", "%6.5i", -1234); TEST(" 1234", "%6.5s", "1234"); +#ifdef INFINITY + TEST("inf", "%f", INFINITY); + TEST("-inf", "%f", -INFINITY); + TEST("INF", "%F", INFINITY); + TEST("-INF", "%F", -INFINITY); + + TEST("inf", "%g", INFINITY); + TEST("-inf", "%g", -INFINITY); + TEST("INF", "%G", INFINITY); + TEST("-INF", "%G", -INFINITY); +#endif + +#ifdef NAN + TEST("nan", "%f", NAN); + TEST("NAN", "%F", NAN); + TEST("nan", "%g", NAN); + TEST("NAN", "%G", NAN); +#endif + #ifdef XSI_TESTS setlocale(LC_ALL, "de_DE"); - + TEST("1.234", "%'u", 1234); TEST("2 1", "%2$u %1$u", 1, 2); #endif - - + + return EXIT_SUCCESS; } -- 1.5.4.5