/* Floating-point printing for `printf'. This is an implementation of a restricted form of the `Dragon4' algorithm described in "How to Print Floating-Point Numbers Accurately", by Guy L. Steele, Jr. and Jon L. White, presented at the ACM SIGPLAN '90 Conference on Programming Language Design and Implementation. Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #define NDEBUG #include #define outchar(x) \ do \ { \ register CONST int outc = (x); \ if (putc (outc, s) == EOF) \ return -1; \ else \ ++done; \ } while (0) #if FLT_RADIX != 2 #error "FLT_RADIX != 2. Write your own __printf_fp." #endif #undef alloca /* gmp-impl.h defines it again. */ #include "gmp.h" #include "gmp-impl.h" #include "longlong.h" #ifndef NDEBUG static void mpn_dump (const char *str, mp_limb *p, mp_size_t size); #define MPN_DUMP(x,y,z) mpn_dump(x,y,z) #else #define MPN_DUMP(x,y,z) #endif extern mp_size_t __mpn_extract_double (mp_ptr res_ptr, mp_size_t size, int *expt, int *is_neg, double value); /* We believe that these variables need as many bits as the largest binary exponent of a double. But we are not confident, so we add a few words. */ #define MPNSIZE ((DBL_MAX_EXP + BITS_PER_MP_LIMB - 1) / BITS_PER_MP_LIMB) + 3 #define MPN_VAR(name) mp_limb name[MPNSIZE]; mp_size_t name##size #define MPN_ASSIGN(dst,src) \ memcpy (dst, src, (dst##size = src##size) * sizeof (mp_limb)) #define MPN_POW2(dst, power) \ do { \ MPN_ZERO (dst, (power) / BITS_PER_MP_LIMB); \ dst[(power) / BITS_PER_MP_LIMB] = \ (mp_limb) 1 << (power) % BITS_PER_MP_LIMB; \ dst##size = (power) / BITS_PER_MP_LIMB + 1; \ } while (0) /* Compare *normalized* mpn vars. */ #define MPN_GT(u,v) \ (u##size > v##size || (u##size == v##size && __mpn_cmp (u, v, u##size) > 0)) #define MPN_LT(u,v) \ (u##size < v##size || (u##size == v##size && __mpn_cmp (u, v, u##size) < 0)) #define MPN_GE(u,v) \ (u##size > v##size || (u##size == v##size && __mpn_cmp (u, v, u##size) >= 0)) #define MPN_LE(u,v) \ (u##size < v##size || (u##size == v##size && __mpn_cmp (u, v, u##size) <= 0)) #define MPN_EQ(u,v) \ (u##size == v##size && __mpn_cmp (u, v, u##size) == 0) #define MPN_NE(u,v) \ (!MPN_EQ(u,v)) int DEFUN(__printf_fp, (s, info, args), FILE *s AND CONST struct printf_info *info AND va_list *args) { mp_limb cy; int done = 0; /* Decimal point character. */ CONST char *CONST decimal = _numeric_info->decimal_point; LONG_DOUBLE fpnum; /* Input. */ int is_neg; MPN_VAR (f); /* Fraction. */ int e; /* Base-2 exponent of the input. */ CONST int p = DBL_MANT_DIG; /* Internal precision. */ MPN_VAR (scale); MPN_VAR (scale2); MPN_VAR (scale10); /* Scale factor. */ MPN_VAR (loerr); MPN_VAR (hierr); /* Potential error in the fraction. */ int k; /* Digits to the left of the decimal point. */ int cutoff; /* Where to stop generating digits. */ MPN_VAR (r); MPN_VAR (r2); MPN_VAR (r10); /* Remainder. */ int roundup; int low, high; char digit; MPN_VAR (tmp); /* Scratch space. */ int j; char type = tolower (info->spec); int prec = info->prec; int width = info->width; /* This algorithm has the nice property of not needing a buffer. However, to get the padding right for %g format, we need to know the length of the number before printing it. */ #ifndef LDBL_DIG #define LDBL_DIG DBL_DIG #endif #ifndef LDBL_MAX_10_EXP #define LDBL_MAX_10_EXP DBL_MAX_10_EXP #endif char *buf = __alloca ((prec > LDBL_DIG ? prec : LDBL_DIG) + LDBL_MAX_10_EXP + 3); /* Dot, e, exp. sign. */ register char *bp = buf; #define put(c) *bp++ = (c) /* Produce the next digit in DIGIT. Return nonzero if it is the last. */ inline int hack_digit (void) { int cnt; mp_limb high_qlimb; --k; cy = __mpn_mul_1 (r10, r, rsize, 10); r10size = rsize; if (cy != 0) r10[r10size++] = cy; MPN_DUMP ("r", r, rsize); MPN_DUMP ("r10", r10, r10size); MPN_DUMP ("scale", scale, scalesize); /* Compute tmp = R10 / scale and R10 = R10 % scale. */ count_leading_zeros (cnt, scale[scalesize - 1]); if (cnt != 0) { mp_limb norm_scale[scalesize]; mp_limb cy; assert (scalesize != 0); __mpn_lshift (norm_scale, scale, scalesize, cnt); assert (r10size != 0); cy = __mpn_lshift (r10, r10, r10size, cnt); if (cy != 0) r10[r10size++] = cy; high_qlimb = __mpn_divmod (tmp, r10, r10size, norm_scale, scalesize); tmp[r10size - scalesize] = high_qlimb; r10size = scalesize; __mpn_rshift (r10, r10, r10size, cnt); } else { high_qlimb = __mpn_divmod (tmp, r10, r10size, scale, scalesize); tmp[r10size - scalesize] = high_qlimb; r10size = scalesize; } MPN_DUMP ("high_qlimb", &high_qlimb, 1); MPN_DUMP ("r10", r10, r10size); /* We should have a quotient < 10. It might be stored */ high_qlimb = tmp[0]; digit = '0' + high_qlimb; r10size = __mpn_normal_size (r10, r10size); if (r10size == 0) /* We are not prepared for an mpn variable with zero limbs. */ r10size = 1; MPN_ASSIGN (r, r10); assert (rsize != 0); cy = __mpn_lshift (r2, r, rsize, 1); r2size = rsize; if (cy != 0) r2[r2size++] = cy; cy = __mpn_mul_1 (loerr, loerr, loerrsize, 10); if (cy) loerr[loerrsize++] = cy; cy = __mpn_mul_1 (hierr, hierr, hierrsize, 10); if (cy) hierr[hierrsize++] = cy; low = MPN_LT (r2, loerr); /* tmp = scale2 - hierr; */ if (scale2size < hierrsize) high = 1; else { cy = __mpn_sub (tmp, scale2, scale2size, hierr, hierrsize); tmpsize = scale2size; high = cy || (roundup ? MPN_GE (r2, tmp) : MPN_GT (r2, tmp)); } if (low || high || k == cutoff) { /* This is confusing, since the text and the code in Steele's and White's paper are contradictory. Problem numbers: printf("%20.15e\n", <1/2^106>) is printed as 1.232595164407830e-32 (instead of 1.232595164407831e-32) if we obey the description in the text; 1/2^330 is badly misprinted if we obey the code. */ if (high && !low) ++digit; #define OBEY_TEXT 1 #if OBEY_TEXT else if (high && low && MPN_GT (r2, scale)) #else else if (high == low && MPN_GT (r2, scale)) #endif ++digit; return 1; } return 0; } const char *special = NULL; /* "NaN" or "Inf" for the special cases. */ /* Fetch the argument value. */ if (info->is_long_double) fpnum = va_arg (*args, LONG_DOUBLE); else fpnum = (LONG_DOUBLE) va_arg (*args, double); /* Check for special values: not a number or infinity. */ if (__isnan ((double) fpnum)) { special = "NaN"; is_neg = 0; } else if (__isinf ((double) fpnum)) { special = "Inf"; is_neg = fpnum < 0; } if (special) { int width = info->prec > info->width ? info->prec : info->width; if (is_neg || info->showsign || info->space) --width; width -= 3; if (!info->left) while (width-- > 0) outchar (' '); if (is_neg) outchar ('-'); else if (info->showsign) outchar ('+'); else if (info->space) outchar (' '); { register size_t len = 3; while (len-- > 0) outchar (*special++); } if (info->left) while (width-- > 0) outchar (' '); return done; } /* Split the number into a fraction and base-2 exponent. The fractional part is scaled by the highest possible number of significant bits of fraction. We represent the fractional part as a (very) large integer. */ fsize = __mpn_extract_double (f, sizeof (f) / sizeof (f[0]), &e, &is_neg, fpnum); if (prec == -1) prec = 6; else if (prec == 0 && type == 'g') prec = 1; if (type == 'g') { if (fpnum != 0) { if (is_neg) fpnum = - fpnum; if (fpnum < 1e-4) type = 'e'; else { /* XXX do this more efficiently */ /* Is (int) floor (log10 (FPNUM)) >= PREC? */ LONG_DOUBLE power = 10; j = prec; if (j > p) j = p; while (--j > 0) { power *= 10; if (fpnum < power) /* log10 (POWER) == floor (log10 (FPNUM)) + 1 log10 (FPNUM) == Number of iterations minus one. */ break; } if (j <= 0) /* We got all the way through the loop and F (i.e., 10**J) never reached FPNUM, so we want to use %e format. */ type = 'e'; } } /* For 'g'/'G' format, the precision specifies "significant digits", not digits to come after the decimal point. */ --prec; } if (fsize == 1 && f[0] == 0) /* Special case for zero. The general algorithm does not work for zero. */ { put ('0'); if (tolower (info->spec) != 'g' || info->alt) { if (prec > 0 || info->alt) put (*decimal); while (prec-- > 0) put ('0'); } if (type == 'e') { put (info->spec); put ('+'); put ('0'); put ('0'); } } else { cutoff = -prec; roundup = 0; if (e > p) { /* The exponent is bigger than the number of fractional digits. */ MPN_ZERO (r, (e - p) / BITS_PER_MP_LIMB); if ((e - p) % BITS_PER_MP_LIMB == 0) { MPN_COPY (r + (e - p) / BITS_PER_MP_LIMB, f, fsize); rsize = fsize + (e - p) / BITS_PER_MP_LIMB; assert (rsize != 0); } else { assert (fsize != 0); cy = __mpn_lshift (r + (e - p) / BITS_PER_MP_LIMB, f, fsize, (e - p) % BITS_PER_MP_LIMB); rsize = fsize + (e - p) / BITS_PER_MP_LIMB; if (cy) r[rsize++] = cy; } MPN_POW2 (scale, 0); assert (scalesize != 0); /* The number is (E - P) factors of two larger than the fraction can represent; this is the potential error. */ MPN_POW2 (loerr, e - p); assert (loerrsize != 0); } else { /* The number of fractional digits is greater than the exponent. Scale by the difference factors of two. */ MPN_ASSIGN (r, f); MPN_POW2 (scale, p - e); MPN_POW2 (loerr, 0); } MPN_ASSIGN (hierr, loerr); /* Fixup. */ MPN_POW2 (tmp, p - 1); if (MPN_EQ (f, tmp)) { /* Account for unequal gaps. */ assert (hierrsize != 0); cy = __mpn_lshift (hierr, hierr, hierrsize, 1); if (cy) hierr[hierrsize++] = cy; assert (rsize != 0); cy = __mpn_lshift (r, r, rsize, 1); if (cy) r[rsize++] = cy; assert (scalesize != 0); cy = __mpn_lshift (scale, scale, scalesize, 1); if (cy) scale[scalesize++] = cy; } /* scale10 = ceil (scale / 10.0). */ if (__mpn_divmod_1 (scale10, scale, scalesize, 10) != 0) { /* We got a remainder. __mpn_divmod_1 has floor'ed the quotient but we want it to be ceil'ed. Adjust. */ cy = __mpn_add_1 (scale10, scale10, scalesize, 1); if (cy) abort (); } scale10size = scalesize; scale10size -= scale10[scale10size - 1] == 0; k = 0; while (MPN_LT (r, scale10)) { mp_limb cy; --k; cy = __mpn_mul_1 (r, r, rsize, 10); if (cy != 0) r[rsize++] = cy; cy = __mpn_mul_1 (loerr, loerr, loerrsize, 10); if (cy != 0) loerr[loerrsize++] = cy; cy = __mpn_mul_1 (hierr, hierr, hierrsize, 10); if (cy != 0) hierr[hierrsize++] = cy; } do { mp_limb cy; assert (rsize != 0); cy = __mpn_lshift (r2, r, rsize, 1); r2size = rsize; if (cy != 0) r2[r2size++] = cy; /* tmp = r2 + hierr; */ if (r2size > hierrsize) { cy = __mpn_add (tmp, r2, r2size, hierr, hierrsize); tmpsize = r2size; } else { cy = __mpn_add (tmp, hierr, hierrsize, r2, r2size); tmpsize = hierrsize; } if (cy != 0) tmp[tmpsize++] = cy; /* while (r2 + hierr >= 2 * scale) */ assert (scalesize != 0); cy = __mpn_lshift (scale2, scale, scalesize, 1); scale2size = scalesize; if (cy) scale2[scale2size++] = cy; while (MPN_GE (tmp, scale2)) { cy = __mpn_mul_1 (scale, scale, scalesize, 10); if (cy) scale[scalesize++] = cy; ++k; assert (scalesize != 0); cy = __mpn_lshift (scale2, scale, scalesize, 1); scale2size = scalesize; if (cy) scale2[scale2size++] = cy; } /* Perform any necessary adjustment of loerr and hierr to take into account the formatting requirements. */ if (type == 'e') cutoff += k - 1; /* CutOffMode == "relative". */ /* Otherwise CutOffMode == "absolute". */ { /* CutOffAdjust. */ int a = cutoff - k; MPN_VAR (y); MPN_ASSIGN (y, scale); /* There is probably a better way to do this. */ while (a > 0) { cy = __mpn_mul_1 (y, y, ysize, 10); if (cy) y[ysize++] = cy; --a; } while (a < 0) { if (__mpn_divmod_1 (y, y, ysize, 10) != 0) { /* We got a remainder. __mpn_divmod_1 has floor'ed the quotient but we want it to be ceil'ed. Adjust. */ cy = __mpn_add_1 (y, y, ysize, 1); if (cy) abort (); } ysize -= y[ysize - 1] == 0; ++a; } if (MPN_GT (y, loerr)) MPN_ASSIGN (loerr, y); if (MPN_GE (y, hierr)) { MPN_ASSIGN (hierr, y); roundup = 1; /* Recalculate: tmp = r2 + hierr */ if (r2size > hierrsize) { cy = __mpn_add (tmp, r2, r2size, hierr, hierrsize); tmpsize = r2size; } else { cy = __mpn_add (tmp, hierr, hierrsize, r2, r2size); tmpsize = hierrsize; } if (cy != 0) tmp[tmpsize++] = cy; } } /* End CutOffAdjust. */ } while (MPN_GE (tmp, scale2)); /* End Fixup. */ /* First digit. */ hack_digit (); if (type == 'e') { /* Exponential notation. */ int expt = k; /* Base-10 exponent. */ int expt_neg; expt_neg = k < 0; if (expt_neg) expt = - expt; /* Find the magnitude of the exponent. */ j = 10; while (j <= expt) j *= 10; /* Write the first digit. */ put (digit); if (low || high || k == cutoff) { if ((tolower (info->spec) != 'g' && prec > 0) || info->alt) put (*decimal); } else { int stop; put (*decimal); /* Remaining digits. */ do { stop = hack_digit (); put (digit); } while (! stop); } if (tolower (info->spec) != 'g' || info->alt) /* Pad with zeros. */ while (--k >= cutoff) put ('0'); /* Write the exponent. */ put (isupper (info->spec) ? 'E' : 'e'); put (expt_neg ? '-' : '+'); if (expt < 10) /* Exponent always has at least two digits. */ put ('0'); do { j /= 10; put ('0' + (expt / j)); expt %= j; } while (j > 1); } else { /* Decimal fraction notation. */ if (k < 0) { put ('0'); if (prec > 0 || info->alt) put (*decimal); /* Write leading fractional zeros. */ j = 0; while (--j > k) put ('0'); } put (digit); if (!low && !high && k != cutoff) { int stop; do { stop = hack_digit (); if (k == -1) put (*decimal); put (digit); } while (! stop); } while (k > 0) { put ('0'); --k; } if ((type != 'g' && prec > 0) || info->alt) { if (k == 0) put (*decimal); while (k-- > -prec) put ('0'); } } } #undef put /* The number is all converted in BUF. Now write it with sign and appropriate padding. */ if (is_neg || info->showsign || info->space) --width; width -= bp - buf; if (!info->left && info->pad == ' ') /* Pad with spaces on the left. */ while (width-- > 0) outchar (' '); /* Write the sign. */ if (is_neg) outchar ('-'); else if (info->showsign) outchar ('+'); else if (info->space) outchar (' '); if (!info->left && info->pad == '0') /* Pad with zeros on the left. */ while (width-- > 0) outchar ('0'); if (fwrite (buf, bp - buf, 1, s) != 1) return -1; done += bp - buf; if (info->left) /* Pad with spaces on the right. */ while (width-- > 0) outchar (' '); return done; } #ifndef NDEBUG static void mpn_dump (str, p, size) const char *str; mp_limb *p; mp_size_t size; { fprintf (stderr, "%s = ", str); while (size != 0) { size--; fprintf (stderr, "%08lX", p[size]); } fprintf (stderr, "\n"); } #endif