Welcome to mirror list, hosted at ThFree Co, Russian Federation.

cygwin.com/git/newlib-cygwin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Yano <takashi.yano@nifty.ne.jp>2021-11-26 00:47:27 +0300
committerTakashi Yano <takashi.yano@nifty.ne.jp>2021-11-26 11:29:40 +0300
commitf885632f4f00c9a4a75d61f4b42c73554a0691b3 (patch)
tree2a9c9c20b9d4d2e0c9137917bba2c0375570870a
parent4f47e64b11ed8d47c62fa89e9b971f44b7e9ab75 (diff)
ldtoa: Fix insufficient valid output digits for "%f" format.
- If the number has large integer part and small fraction part is specified in output format, e.g. printf("%.3f", sqrt(2)*1e60);, valid output digits were insufficient. This patch fixes the issue.
-rw-r--r--newlib/libc/stdlib/ldtoa.c41
1 files changed, 25 insertions, 16 deletions
diff --git a/newlib/libc/stdlib/ldtoa.c b/newlib/libc/stdlib/ldtoa.c
index 1b32e801c..26c61948b 100644
--- a/newlib/libc/stdlib/ldtoa.c
+++ b/newlib/libc/stdlib/ldtoa.c
@@ -83,7 +83,7 @@ static void eclear (register short unsigned int *x);
static void einfin (register short unsigned int *x, register LDPARMS * ldp);
static void efloor (short unsigned int *x, short unsigned int *y,
LDPARMS * ldp);
-static void etoasc (short unsigned int *x, char *string, int ndigs,
+static void etoasc (short unsigned int *x, char *string, int ndec, int ndigs,
int outformat, LDPARMS * ldp);
union uconv
@@ -217,7 +217,7 @@ static const char *const ermsg[7] = {
* e24toasc( &f, str, n ) single to ASCII string, n digits after decimal
* e53toasc( &d, str, n ) double to ASCII string, n digits after decimal
* e64toasc( &d, str, n ) long double to ASCII string
- * etoasc(e,str,n,fmt,ldp)e to ASCII string, n digits after decimal
+ * etoasc(e,str,ndec,n,fmt,ldp)e to ASCII string, n digits after decimal
* etoe24( e, &f ) convert e type to IEEE single precision
* etoe53( e, &d ) convert e type to IEEE double precision
* etoe64( e, &d ) convert e type to IEEE long double precision
@@ -2839,23 +2839,33 @@ _ldtoa_r (struct _reent *ptr, long double d, int mode, int ndigits,
/* This sanity limit must agree with the corresponding one in etoasc, to
keep straight the returned value of outexpon. Note that we use a dynamic
- limit now, either NDEC or NDEC_SML, depending on ndigits. See the usage
- of "my_NDEC" in etoasc. */
- if (ndigits > NDEC)
- ndigits = NDEC;
+ limit now, either ndec (<= NDEC) or NDEC_SML, depending on ndigits. */
+ __int32_t ndec;
+ if (mode == 3) /* %f */
+ {
+ __int32_t expon = (e[NE - 1] & 0x7fff) - (EXONE - 1); /* exponent part */
+ /* log2(10) approximately 485/146 */
+ ndec = expon * 146 / 485 + ndigits;
+ }
+ else /* %g/%e */
+ ndec = ndigits;
+ if (ndec < 0)
+ ndec = 0;
+ if (ndec > NDEC)
+ ndec = NDEC;
/* Allocate buffer if more than NDEC_SML digits are requested. */
- if (ndigits > NDEC_SML)
+ if (ndec > NDEC_SML)
{
- outbuf = (char *) _malloc_r (ptr, NDEC + MAX_EXP_DIGITS + 10);
+ outbuf = (char *) _malloc_r (ptr, ndec + MAX_EXP_DIGITS + 10);
if (!outbuf)
{
- ndigits = NDEC_SML;
+ ndec = NDEC_SML;
outbuf = outbuf_sml;
}
}
- etoasc (e, outbuf, ndigits, mode, ldp);
+ etoasc (e, outbuf, (int) ndec, ndigits, mode, ldp);
s = outbuf;
if (eisinf (e) || eisnan (e))
{
@@ -2992,8 +3002,8 @@ _ldcheck (long double *d)
} /* _ldcheck */
static void
-etoasc (short unsigned int *x, char *string, int ndigits, int outformat,
- LDPARMS * ldp)
+etoasc (short unsigned int *x, char *string, int ndec, int ndigits,
+ int outformat, LDPARMS * ldp)
{
long digit;
unsigned short y[NI], t[NI], u[NI], w[NI];
@@ -3003,7 +3013,6 @@ etoasc (short unsigned int *x, char *string, int ndigits, int outformat,
char *s, *ss;
unsigned short m;
unsigned short *equot = ldp->equot;
- int my_NDEC = (ndigits > NDEC_SML) ? NDEC : NDEC_SML;
ndigs = ndigits;
rndsav = ldp->rndprc;
@@ -3129,7 +3138,7 @@ tnzro:
else
{
emovi (y, w);
- for (i = 0; i < my_NDEC + 1; i++)
+ for (i = 0; i < ndec + 1; i++)
{
if ((w[NI - 1] & 0x7) != 0)
break;
@@ -3205,8 +3214,8 @@ isone:
else if( ndigs < 0 )
ndigs = 0;
*/
- if (ndigs > my_NDEC)
- ndigs = my_NDEC;
+ if (ndigs > ndec)
+ ndigs = ndec;
if (digit == 10)
{
*s++ = '1';