275 lines
7.9 KiB
Diff
275 lines
7.9 KiB
Diff
From d10d2f6fca4e391fa863d771dd8c5f7c01d7dfa4 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 12/18] 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.6.2.5
|
|
|