📄 printf_fp.c
字号:
/* Floating point output for `printf'. Copyright (C) 1995-1999,2000,2001,2002,2003 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *//* The gmp headers need some configuration frobs. */#define HAVE_ALLOCA 1#include <libioP.h>#include <alloca.h>#include <ctype.h>#include <float.h>#include <gmp-mparam.h>#include <gmp.h>#include <stdlib/gmp-impl.h>#include <stdlib/longlong.h>#include <stdlib/fpioconst.h>#include <locale/localeinfo.h>#include <limits.h>#include <math.h>#include <printf.h>#include <string.h>#include <unistd.h>#include <stdlib.h>#include <wchar.h>#ifdef COMPILE_WPRINTF# define CHAR_T wchar_t#else# define CHAR_T char#endif#include "_i18n_number.h"#ifndef NDEBUG# define NDEBUG /* Undefine this for debugging assertions. */#endif#include <assert.h>/* This defines make it possible to use the same code for GNU C library and the GNU I/O library. */#define PUT(f, s, n) _IO_sputn (f, s, n)#define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : INTUSE(_IO_padn) (f, c, n))/* We use this file GNU C library and GNU I/O library. So make names equal. */#undef putc#define putc(c, f) (wide \ ? (int)_IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))#define size_t _IO_size_t#define FILE _IO_FILE/* Macros for doing the actual output. */#define outchar(ch) \ do \ { \ register const int outc = (ch); \ if (putc (outc, fp) == EOF) \ return -1; \ ++done; \ } while (0)#define PRINT(ptr, wptr, len) \ do \ { \ register size_t outlen = (len); \ if (len > 20) \ { \ if (PUT (fp, wide ? (const char *) wptr : ptr, outlen) != outlen) \ return -1; \ ptr += outlen; \ done += outlen; \ } \ else \ { \ if (wide) \ while (outlen-- > 0) \ outchar (*wptr++); \ else \ while (outlen-- > 0) \ outchar (*ptr++); \ } \ } while (0)#define PADN(ch, len) \ do \ { \ if (PAD (fp, ch, len) != len) \ return -1; \ done += len; \ } \ while (0)/* We use the GNU MP library to handle large numbers. An MP variable occupies a varying number of entries in its array. We keep track of this number for efficiency reasons. Otherwise we would always have to process the whole array. */#define MPN_VAR(name) mp_limb_t *name; mp_size_t name##size#define MPN_ASSIGN(dst,src) \ memcpy (dst, src, (dst##size = src##size) * sizeof (mp_limb_t))#define MPN_GE(u,v) \ (u##size > v##size || (u##size == v##size && __mpn_cmp (u, v, u##size) >= 0))extern int __isinfl_internal (long double) attribute_hidden;extern int __isnanl_internal (long double) attribute_hidden;extern mp_size_t __mpn_extract_double (mp_ptr res_ptr, mp_size_t size, int *expt, int *is_neg, double value);extern mp_size_t __mpn_extract_long_double (mp_ptr res_ptr, mp_size_t size, int *expt, int *is_neg, long double value);extern unsigned int __guess_grouping (unsigned int intdig_max, const char *grouping);static wchar_t *group_number (wchar_t *buf, wchar_t *bufend, unsigned int intdig_no, const char *grouping, wchar_t thousands_sep, int ngroups) internal_function;int__printf_fp (FILE *fp, const struct printf_info *info, const void *const *args){ /* The floating-point value to output. */ union { double dbl; __long_double_t ldbl; } fpnum; /* Locale-dependent representation of decimal point. */ const char *decimal; wchar_t decimalwc; /* Locale-dependent thousands separator and grouping specification. */ const char *thousands_sep = NULL; wchar_t thousands_sepwc = 0; const char *grouping; /* "NaN" or "Inf" for the special cases. */ const char *special = NULL; const wchar_t *wspecial = NULL; /* We need just a few limbs for the input before shifting to the right position. */ mp_limb_t fp_input[(LDBL_MANT_DIG + BITS_PER_MP_LIMB - 1) / BITS_PER_MP_LIMB]; /* We need to shift the contents of fp_input by this amount of bits. */ int to_shift = 0; /* The fraction of the floting-point value in question */ MPN_VAR(frac); /* and the exponent. */ int exponent; /* Sign of the exponent. */ int expsign = 0; /* Sign of float number. */ int is_neg = 0; /* Scaling factor. */ MPN_VAR(scale); /* Temporary bignum value. */ MPN_VAR(tmp); /* Digit which is result of last hack_digit() call. */ wchar_t digit; /* The type of output format that will be used: 'e'/'E' or 'f'. */ int type; /* Counter for number of written characters. */ int done = 0; /* General helper (carry limb). */ mp_limb_t cy; /* Nonzero if this is output on a wide character stream. */ int wide = info->wide; auto wchar_t hack_digit (void); wchar_t hack_digit (void) { mp_limb_t hi; if (expsign != 0 && type == 'f' && exponent-- > 0) hi = 0; else if (scalesize == 0) { hi = frac[fracsize - 1]; frac[fracsize - 1] = __mpn_mul_1 (frac, frac, fracsize - 1, 10); } else { if (fracsize < scalesize) hi = 0; else { hi = mpn_divmod (tmp, frac, fracsize, scale, scalesize); tmp[fracsize - scalesize] = hi; hi = tmp[0]; fracsize = scalesize; while (fracsize != 0 && frac[fracsize - 1] == 0) --fracsize; if (fracsize == 0) { /* We're not prepared for an mpn variable with zero limbs. */ fracsize = 1; return L'0' + hi; } } mp_limb_t _cy = __mpn_mul_1 (frac, frac, fracsize, 10); if (_cy != 0) frac[fracsize++] = _cy; } return L'0' + hi; } /* Figure out the decimal point character. */ if (info->extra == 0) { decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT); decimalwc = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC); } else { decimal = _NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT); if (*decimal == '\0') decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT); decimalwc = _NL_CURRENT_WORD (LC_MONETARY, _NL_MONETARY_DECIMAL_POINT_WC); if (decimalwc == L'\0') decimalwc = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC); } /* The decimal point character must not be zero. */ assert (*decimal != '\0'); assert (decimalwc != L'\0'); if (info->group) { if (info->extra == 0) grouping = _NL_CURRENT (LC_NUMERIC, GROUPING); else grouping = _NL_CURRENT (LC_MONETARY, MON_GROUPING); if (*grouping <= 0 || *grouping == CHAR_MAX) grouping = NULL; else { /* Figure out the thousands separator character. */ if (wide) { if (info->extra == 0) thousands_sepwc = _NL_CURRENT_WORD (LC_NUMERIC, _NL_NUMERIC_THOUSANDS_SEP_WC); else thousands_sepwc = _NL_CURRENT_WORD (LC_MONETARY, _NL_MONETARY_THOUSANDS_SEP_WC); } else { if (info->extra == 0) thousands_sep = _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP); else thousands_sep = _NL_CURRENT (LC_MONETARY, MON_THOUSANDS_SEP); } if ((wide && thousands_sepwc == L'\0') || (! wide && *thousands_sep == '\0')) grouping = NULL; else if (thousands_sepwc == L'\0') /* If we are printing multibyte characters and there is a multibyte representation for the thousands separator, we must ensure the wide character thousands separator is available, even if it is fake. */ thousands_sepwc = 0xfffffffe; } } else grouping = NULL; /* Fetch the argument value. */#ifndef __NO_LONG_DOUBLE_MATH if (info->is_long_double && sizeof (long double) > sizeof (double)) { fpnum.ldbl = *(const long double *) args[0]; /* Check for special values: not a number or infinity. */ if (__isnanl (fpnum.ldbl)) { if (isupper (info->spec)) { special = "NAN"; wspecial = L"NAN"; } else { special = "nan"; wspecial = L"nan"; } is_neg = 0; } else if (__isinfl (fpnum.ldbl)) { if (isupper (info->spec)) { special = "INF"; wspecial = L"INF"; } else { special = "inf"; wspecial = L"inf"; } is_neg = fpnum.ldbl < 0; } else { fracsize = __mpn_extract_long_double (fp_input, (sizeof (fp_input) / sizeof (fp_input[0])), &exponent, &is_neg, fpnum.ldbl); to_shift = 1 + fracsize * BITS_PER_MP_LIMB - LDBL_MANT_DIG; } } else#endif /* no long double */ { fpnum.dbl = *(const double *) args[0]; /* Check for special values: not a number or infinity. */ if (__isnan (fpnum.dbl)) { is_neg = 0; if (isupper (info->spec)) { special = "NAN"; wspecial = L"NAN"; } else { special = "nan"; wspecial = L"nan"; } } else if (__isinf (fpnum.dbl)) { is_neg = fpnum.dbl < 0; if (isupper (info->spec)) { special = "INF"; wspecial = L"INF"; } else { special = "inf"; wspecial = L"inf"; } } else { fracsize = __mpn_extract_double (fp_input, (sizeof (fp_input) / sizeof (fp_input[0])), &exponent, &is_neg, fpnum.dbl); to_shift = 1 + fracsize * BITS_PER_MP_LIMB - DBL_MANT_DIG; } } if (special) { int width = info->width; if (is_neg || info->showsign || info->space) --width; width -= 3; if (!info->left && width > 0) PADN (' ', width); if (is_neg) outchar ('-'); else if (info->showsign) outchar ('+');
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -