From 50a8647d6402d6b6f547b9d77d9ab1b6679a578d Mon Sep 17 00:00:00 2001 From: patacongo Date: Mon, 6 Aug 2012 02:14:36 +0000 Subject: Fix a floating point presentation error git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5012 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/ChangeLog | 5 + nuttx/TODO | 8 +- nuttx/lib/stdio/lib_libdtoa.c | 306 +++++++++++++----------------------------- 3 files changed, 102 insertions(+), 217 deletions(-) diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog index 6fd497943..d57d9c4d8 100644 --- a/nuttx/ChangeLog +++ b/nuttx/ChangeLog @@ -3125,3 +3125,8 @@ * lib/stdio/lib_libvsprintf.c: Fieldwidth and justification were not supported for the %s format. As a result, %s, %12s, and %-12s all produced the same output. + * lib/stdio/lib_libdtoa.c: Fix several issues with presenting floating + point numbers (conversions are fine, but presentation was bad). This + is a critical bug fix if you use printf or sprintf to deal with floating + point numbers. + diff --git a/nuttx/TODO b/nuttx/TODO index e2abce7f4..087977aee 100644 --- a/nuttx/TODO +++ b/nuttx/TODO @@ -15,7 +15,7 @@ nuttx/ (5) Binary loaders (binfmt/) (17) Network (net/, drivers/net) (3) USB (drivers/usbdev, drivers/usbhost) - (9) Libraries (lib/) + (10) Libraries (lib/) (10) File system/Generic drivers (fs/, drivers/) (5) Graphics subystem (graphics/) (1) Pascal add-on (pcode/) @@ -685,6 +685,12 @@ o Libraries (lib/) Status: Open Priority: Low -- more of a roadmap + Title: FLOATING POINT FORMATS + Description: Only the %f floating point format is supported. Others are accepted + but treated like %f. + Status: Open + Priority: Medium (this might important to someone. + o File system / Generic drivers (fs/, drivers/) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/nuttx/lib/stdio/lib_libdtoa.c b/nuttx/lib/stdio/lib_libdtoa.c index b805d13a7..2d4309abe 100644 --- a/nuttx/lib/stdio/lib_libdtoa.c +++ b/nuttx/lib/stdio/lib_libdtoa.c @@ -48,7 +48,13 @@ * Pre-processor Definitions ****************************************************************************/ -#define MAXEXP 308 +#ifndef MIN +# define MIN(a,b) (a < b ? a : b) +#endif + +#ifndef MAX +# define MAX(a,b) (a > b ? a : b) +#endif /**************************************************************************** * Private Type Declarations @@ -58,10 +64,6 @@ * Private Function Prototypes ****************************************************************************/ -static char* cvt(double value, int ndigits, int flags, char *sign, - int *decpt, int ch, int *length); -static int exponent(char *p0, int exp, int fmtch); - /**************************************************************************** * Global Constant Data ****************************************************************************/ @@ -79,268 +81,122 @@ static int exponent(char *p0, int exp, int fmtch); ****************************************************************************/ /**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: cvt + * Name: zeroes + * + * Description: + * Print the specified number of zeres + * ****************************************************************************/ -static char* cvt(double value, int ndigits, int flags, char *sign, - int *decpt, int ch, int *length) +static void zeroes(FAR struct lib_outstream_s *obj, int nzeroes) { - int mode, dsgn; - char *digits, *bp, *rve; - - if (ch == 'f') - { - mode = 3; /* ndigits after the decimal point */ - } - else - { - /* To obtain ndigits after the decimal point for the 'e' and 'E' - * formats, round to ndigits + 1 significant figures. - */ - - if (ch == 'e' || ch == 'E') - { - ndigits++; - } - mode = 2; /* ndigits significant digits */ - } - - if (value < 0) - { - value = -value; - *sign = '-'; - } - else - { - *sign = '\000'; - } + int i; - digits = __dtoa(value, mode, ndigits, decpt, &dsgn, &rve); - if ((ch != 'g' && ch != 'G') || IS_ALTFORM(flags)) + for (i = nzeroes; i > 0; i--) { - /* Print trailing zeros */ - - bp = digits + ndigits; - if (ch == 'f') - { - if (*digits == '0' && value) - { - *decpt = -ndigits + 1; - } - bp += *decpt; - } - - if (value == 0) - { - /* kludge for __dtoa irregularity */ - - rve = bp; - } - - while (rve < bp) - { - *rve++ = '0'; - } + obj->put(obj, '0'); } - - *length = rve - digits; - return digits; } /**************************************************************************** - * Name: exponent + * Private Functions ****************************************************************************/ -static int exponent(FAR char *p0, int exp, int fmtch) -{ - FAR char *p; - FAR char *t; - char expbuf[MAXEXP]; - - p = p0; - *p++ = fmtch; - if (exp < 0) - { - exp = -exp; - *p++ = '-'; - } - else - { - *p++ = '+'; - } - - t = expbuf + MAXEXP; - if (exp > 9) - { - do - { - *--t = (exp % 10) + '0'; - } - while ((exp /= 10) > 9); - *--t = exp + '0'; - for (; t < expbuf + MAXEXP; *p++ = *t++); - } - else - { - *p++ = '0'; - *p++ = exp + '0'; - } - return (p - p0); -} - /**************************************************************************** * Name: lib_dtoa * * Description: * This is part of lib_vsprintf(). It handles the floating point formats. + * This version supports only the &f (with precision). * ****************************************************************************/ -static void lib_dtoa(FAR struct lib_outstream_s *obj, int ch, int prec, - uint8_t flags, double _double) +static void lib_dtoa(FAR struct lib_outstream_s *obj, int fmt, int prec, + uint8_t flags, double value) { - FAR char *cp; /* Handy char pointer (short term usage) */ - FAR char *cp_free = NULL; /* BIONIC: copy of cp to be freed after usage */ - char expstr[7]; /* Buffer for exponent string */ - char sign; /* Temporary negative sign for floats */ - int expt; /* Integer value of exponent */ - int expsize = 0; /* Character count for expstr */ - int ndig; /* Actual number of digits returned by cvt */ - int size; /* Size of converted field or string */ + FAR char *digits; /* String returned by __dtoa */ + FAR char *digalloc; /* Copy of digits to be freed after usage */ + FAR char *rve; /* Points to the end of the return value */ + char sign; /* Temporary negative sign for floats */ + int expt; /* Integer value of exponent */ + int numlen; /* Actual number of digits returned by cvt */ + int nchars; /* Number of characters to print */ + int dsgn; /* Unused sign indicator */ int i; - cp = cvt(_double, prec, flags, &sign, &expt, ch, &ndig); - cp_free = cp; + /* Non-zero... positive or negative */ - if (ch == 'g' || ch == 'G') + if (value < 0) { - /* 'g' or 'G' fmt */ - - if (expt <= -4 || expt > prec) - { - ch = (ch == 'g') ? 'e' : 'E'; - } - else - { - ch = 'g'; - } + value = -value; + sign = '-'; } - - if (ch <= 'e') + else { - /* 'e' or 'E' fmt */ - - --expt; - expsize = exponent(expstr, expt, ch); - size = expsize + ndig; - if (ndig > 1 || IS_ALTFORM(flags)) - { - ++size; - } + sign = '\0'; } - else if (ch == 'f') - { - /* f fmt */ - if (expt > 0) - { - size = expt; - if (prec || IS_ALTFORM(flags)) - { - size += prec + 1; - } - } - else /* "0.X" */ - { - size = prec + 2; - } - } - else if (expt >= ndig) - { - /* fixed g fmt */ + /* Perform the conversion */ - size = expt; - if (IS_ALTFORM(flags)) - { - ++size; - } - } - else - { - size = ndig + (expt > 0 ? 1 : 2 - expt); - } + digits = __dtoa(value, 3, prec, &expt, &dsgn, &rve); + digalloc = digits; + numlen = rve - digits; if (sign) { obj->put(obj, '-'); } - if (_double == 0) + /* Always print at least one digit to the right of the decimal point. */ + + prec = MAX(1, prec); + + /* Special case exact zero or the case where the number is smaller than + * the print precision. + */ + + if (value == 0 || expt < -prec) { /* kludge for __dtoa irregularity */ obj->put(obj, '0'); - if (expt < ndig || IS_ALTFORM(flags)) - { - obj->put(obj, '.'); - - i = ndig - 1; - while (i > 0) - { - obj->put(obj, '0'); - i--; - } - } + obj->put(obj, '.'); } else if (expt <= 0) { obj->put(obj, '0'); obj->put(obj, '.'); - i = ndig; - while (i > 0) - { - obj->put(obj, *cp); - i--; - cp++; - } - } - else if (expt >= ndig) - { - i = ndig; - while (i > 0) - { - obj->put(obj, *cp); - i--; - cp++; - } + /* Print leading zeros */ - i = expt - ndig; - while (i > 0) + if (expt < 0) { - obj->put(obj, '0'); - i--; + nchars = MIN(-expt, prec); + zeroes(obj, nchars); + prec -= nchars; } + + /* Print the significant digits */ - if (IS_ALTFORM(flags)) + nchars = MIN(numlen, prec); + for (i = nchars; i > 0; i--) { - obj->put(obj, '.'); + obj->put(obj, *digits); + digits++; } + + /* Decremnt to get the number of trailing zeroes to print */ + + prec -= nchars; } else { - /* Print the integer */ + /* Print the integer part */ - i = expt; - while (i > 0) + for (i = expt; i > 0; i--) { - obj->put(obj, *cp); - i--; - cp++; + obj->put(obj, *digits); + digits++; } /* Print the decimal place */ @@ -349,14 +205,32 @@ static void lib_dtoa(FAR struct lib_outstream_s *obj, int ch, int prec, /* Print the decimal */ - i = ndig - expt; - while (i > 0) + numlen -= expt; + nchars = MIN(numlen, prec); + + for (i = nchars; i > 0; i--) { - obj->put(obj, *cp); - i--; - cp++; + obj->put(obj, *digits); + digits++; } + + /* Decremnt to get the number of trailing zeroes to print */ + + prec -= nchars; + } + + /* Finally, print any trailing zeroes */ + + zeroes(obj, prec); + + /* Is this memory supposed to be freed or not? */ + +#if 0 + if (digalloc) + { + free(digalloc); } +#endif } /**************************************************************************** -- cgit v1.2.3