📄 printf_fp.c
字号:
/* 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/ormodify it under the terms of the GNU Library General Public License aspublished by the Free Software Foundation; either version 2 of theLicense, 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 ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNULibrary General Public License for more details.You should have received a copy of the GNU Library General PublicLicense along with the GNU C Library; see the file COPYING.LIB. Ifnot, write to the Free Software Foundation, Inc., 675 Mass Ave,Cambridge, MA 02139, USA. */#include <ansidecl.h>#include <ctype.h>#include <stdio.h>#include <float.h>#include <limits.h>#include <math.h>#include <stdarg.h>#include <stdlib.h>#include <localeinfo.h>#include <printf.h>#define NDEBUG#include <assert.h>#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 NDEBUGstatic 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)#endifextern 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))intDEFUN(__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 ('+');
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -