libs/libc/stdio: Fix the %f floating point output format.

This commit is contained in:
Gregory Nutt 2019-02-15 09:56:58 -06:00
parent 7b9d02d496
commit eb0223bc7f
3 changed files with 50 additions and 24 deletions

View File

@ -121,9 +121,8 @@ config NANO_PRINTF
depends on !LIBC_LONG_LONG
---help---
Replace printf code with version from newlib-nano. This version
provides improved floating point output support, including 'g' mode
as well as making the default 'f' format include digits past the
decimal point. However, it does not include 'long long' support.
provides adds support for the 'g' format. However, it does not
include 'long long' support.
config NANO_PRINTLEVEL
int "Nano printf support level"

View File

@ -46,6 +46,7 @@
#include <nuttx/config.h>
#include <stdbool.h>
#include <math.h>
#include <assert.h>
@ -67,6 +68,17 @@
# define MAX(a,b) (a > b ? a : b)
#endif
/* Use (almost) the maximim precision with %f format if no precision is
* specified. We do not use the full precision beause the least significant
* digits are probably garbage.
*
* REVISIT: This should be smarter. 15 digits is the maximum size of the
* number. The maximum precision is really 15 minus the number of digits
* in the integer part.
*/
#define DOUBLE_PRECISON_MAX 13 /* vs 15 which is the maximum */
/****************************************************************************
* Private Functions
****************************************************************************/
@ -132,6 +144,7 @@ static void lib_dtoa(FAR struct lib_outstream_s *obj, int fmt, int prec,
{
FAR char *digits; /* String returned by __dtoa */
FAR char *rve; /* Points to the end of the return value */
bool hasdot; /* True: precision specified */
int expt; /* Integer value of exponent */
int numlen; /* Actual number of digits returned by cvt */
int nchars; /* Number of characters to print */
@ -149,6 +162,14 @@ static void lib_dtoa(FAR struct lib_outstream_s *obj, int fmt, int prec,
DEBUGASSERT(up_interrupt_context() == false);
#endif
/* Set to default precision if none specified */
hasdot = IS_HASDOT(flags);
if (!hasdot && prec == 0)
{
prec = DOUBLE_PRECISON_MAX;
}
/* Special handling for NaN and Infinity */
if (isnan(value))
@ -178,8 +199,8 @@ static void lib_dtoa(FAR struct lib_outstream_s *obj, int fmt, int prec,
/* Perform the conversion */
digits = __dtoa(value, 3, prec, &expt, &dsgn, &rve);
numlen = rve - digits;
digits = __dtoa(value, 3, prec, &expt, &dsgn, &rve);
numlen = rve - digits;
/* Avoid precision error from missing trailing zeroes */
@ -198,7 +219,7 @@ static void lib_dtoa(FAR struct lib_outstream_s *obj, int fmt, int prec,
* the print precision.
*/
if (value == 0 || expt < -prec)
if (value == 0 || (expt < (hasdot ? -prec : 0)))
{
/* kludge for __dtoa irregularity */
@ -208,13 +229,20 @@ static void lib_dtoa(FAR struct lib_outstream_s *obj, int fmt, int prec,
* particular precision is requested.
*/
if (prec > 0 || IS_ALTFORM(flags))
if ((prec > 0 && hasdot) || IS_ALTFORM(flags))
{
obj->put(obj, '.');
/* Always print at least one digit to the right of the decimal point. */
prec = MAX(1, prec);
if (hasdot)
{
prec = MAX(1, prec);
}
else
{
prec = MAX(1, numlen);
}
}
}
@ -222,7 +250,6 @@ static void lib_dtoa(FAR struct lib_outstream_s *obj, int fmt, int prec,
else
{
/* Handle the case where the value is less than 1.0 (in magnitude) and
* will need a leading zero.
*/
@ -239,7 +266,7 @@ static void lib_dtoa(FAR struct lib_outstream_s *obj, int fmt, int prec,
/* Print any leading zeros to the right of the decimal point */
if (expt < 0)
if (expt < 0 || hasdot)
{
nchars = MIN(-expt, prec);
zeroes(obj, nchars);
@ -277,7 +304,8 @@ static void lib_dtoa(FAR struct lib_outstream_s *obj, int fmt, int prec,
* requested.
*/
if (numlen > 0 || prec > 0 || IS_ALTFORM(flags))
if (numlen > 0 || (prec > 0 && hasdot) ||
IS_ALTFORM(flags))
{
/* Print the decimal point */
@ -287,7 +315,14 @@ static void lib_dtoa(FAR struct lib_outstream_s *obj, int fmt, int prec,
* point.
*/
prec = MAX(1, prec);
if (hasdot)
{
prec = MAX(1, prec);
}
else
{
prec = MAX(1, numlen);
}
}
}
@ -319,7 +354,10 @@ static void lib_dtoa(FAR struct lib_outstream_s *obj, int fmt, int prec,
/* Finally, print any trailing zeroes */
zeroes(obj, prec);
if (hasdot)
{
zeroes(obj, prec);
}
}
/****************************************************************************

View File

@ -118,10 +118,6 @@
# define FMT_PREV src-- /* Backup to the previous character */
#endif
/* Default precision to use with %f format if no precision is specified. */
#define FLOAT_PRECISION_DEFAULT 6
/****************************************************************************
* Private Type Declarations
****************************************************************************/
@ -1559,13 +1555,6 @@ int lib_vsprintf(FAR struct lib_outstream_s *obj, FAR const IPTR char *src,
double dblval = va_arg(ap, double);
int dblsize;
/* Set to default precision if none specified */
if (!IS_HASDOT(flags) && trunc == 0)
{
trunc = FLOAT_PRECISION_DEFAULT;
}
/* Get the width of the output */
dblsize = getdblsize(FMT_CHAR, trunc, flags, dblval);