📄 vasnprintf.c
字号:
/* -*- buffer-read-only: t -*- vi: set ro: *//* DO NOT EDIT! GENERATED AUTOMATICALLY! *//* vsprintf with automatic memory allocation. Copyright (C) 1999, 2002-2008 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *//* This file can be parametrized with the following macros: VASNPRINTF The name of the function being defined. FCHAR_T The element type of the format string. DCHAR_T The element type of the destination (result) string. FCHAR_T_ONLY_ASCII Set to 1 to enable verification that all characters in the format string are ASCII. MUST be set if FCHAR_T and DCHAR_T are not the same type. DIRECTIVE Structure denoting a format directive. Depends on FCHAR_T. DIRECTIVES Structure denoting the set of format directives of a format string. Depends on FCHAR_T. PRINTF_PARSE Function that parses a format string. Depends on FCHAR_T. DCHAR_CPY memcpy like function for DCHAR_T[] arrays. DCHAR_SET memset like function for DCHAR_T[] arrays. DCHAR_MBSNLEN mbsnlen like function for DCHAR_T[] arrays. SNPRINTF The system's snprintf (or similar) function. This may be either snprintf or swprintf. TCHAR_T The element type of the argument and result string of the said SNPRINTF function. This may be either char or wchar_t. The code exploits that sizeof (TCHAR_T) | sizeof (DCHAR_T) and alignof (TCHAR_T) <= alignof (DCHAR_T). DCHAR_IS_TCHAR Set to 1 if DCHAR_T and TCHAR_T are the same type. DCHAR_CONV_FROM_ENCODING A function to convert from char[] to DCHAR[]. DCHAR_IS_UINT8_T Set to 1 if DCHAR_T is uint8_t. DCHAR_IS_UINT16_T Set to 1 if DCHAR_T is uint16_t. DCHAR_IS_UINT32_T Set to 1 if DCHAR_T is uint32_t. *//* Tell glibc's <stdio.h> to provide a prototype for snprintf(). This must come before <config.h> because <config.h> may include <features.h>, and once <features.h> has been included, it's too late. */#ifndef _GNU_SOURCE# define _GNU_SOURCE 1#endif#ifndef VASNPRINTF# include <config.h>#endif#ifndef IN_LIBINTL# include <alloca.h>#endif/* Specification. */#ifndef VASNPRINTF# if WIDE_CHAR_VERSION# include "vasnwprintf.h"# else# include "vasnprintf.h"# endif#endif#include <locale.h> /* localeconv() */#include <stdio.h> /* snprintf(), sprintf() */#include <stdlib.h> /* abort(), malloc(), realloc(), free() */#include <string.h> /* memcpy(), strlen() */#include <errno.h> /* errno */#include <limits.h> /* CHAR_BIT */#include <float.h> /* DBL_MAX_EXP, LDBL_MAX_EXP */#if HAVE_NL_LANGINFO# include <langinfo.h>#endif#ifndef VASNPRINTF# if WIDE_CHAR_VERSION# include "wprintf-parse.h"# else# include "printf-parse.h"# endif#endif/* Checked size_t computations. */#include "xsize.h"#if (NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE) && !defined IN_LIBINTL# include <math.h># include "float+.h"#endif#if (NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL# include <math.h># include "isnand.h"#endif#if (NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE) && !defined IN_LIBINTL# include <math.h># include "isnanl-nolibm.h"# include "fpucw.h"#endif#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL# include <math.h># include "isnand.h"# include "printf-frexp.h"#endif#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE) && !defined IN_LIBINTL# include <math.h># include "isnanl-nolibm.h"# include "printf-frexpl.h"# include "fpucw.h"#endif#if HAVE_WCHAR_T# if HAVE_WCSLEN# define local_wcslen wcslen# else /* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid a dependency towards this library, here is a local substitute. Define this substitute only once, even if this file is included twice in the same compilation unit. */# ifndef local_wcslen_defined# define local_wcslen_defined 1static size_tlocal_wcslen (const wchar_t *s){ const wchar_t *ptr; for (ptr = s; *ptr != (wchar_t) 0; ptr++) ; return ptr - s;}# endif# endif#endif/* Default parameters. */#ifndef VASNPRINTF# if WIDE_CHAR_VERSION# define VASNPRINTF vasnwprintf# define FCHAR_T wchar_t# define DCHAR_T wchar_t# define TCHAR_T wchar_t# define DCHAR_IS_TCHAR 1# define DIRECTIVE wchar_t_directive# define DIRECTIVES wchar_t_directives# define PRINTF_PARSE wprintf_parse# define DCHAR_CPY wmemcpy# else# define VASNPRINTF vasnprintf# define FCHAR_T char# define DCHAR_T char# define TCHAR_T char# define DCHAR_IS_TCHAR 1# define DIRECTIVE char_directive# define DIRECTIVES char_directives# define PRINTF_PARSE printf_parse# define DCHAR_CPY memcpy# endif#endif#if WIDE_CHAR_VERSION /* TCHAR_T is wchar_t. */# define USE_SNPRINTF 1# if HAVE_DECL__SNWPRINTF /* On Windows, the function swprintf() has a different signature than on Unix; we use the _snwprintf() function instead. */# define SNPRINTF _snwprintf# else /* Unix. */# define SNPRINTF swprintf# endif#else /* TCHAR_T is char. */# /* Use snprintf if it exists under the name 'snprintf' or '_snprintf'. But don't use it on BeOS, since BeOS snprintf produces no output if the size argument is >= 0x3000000. */# if (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF) && !defined __BEOS__# define USE_SNPRINTF 1# else# define USE_SNPRINTF 0# endif# if HAVE_DECL__SNPRINTF /* Windows. */# define SNPRINTF _snprintf# else /* Unix. */# define SNPRINTF snprintf /* Here we need to call the native snprintf, not rpl_snprintf. */# undef snprintf# endif#endif/* Here we need to call the native sprintf, not rpl_sprintf. */#undef sprintf#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL/* Determine the decimal-point character according to the current locale. */# ifndef decimal_point_char_defined# define decimal_point_char_defined 1static chardecimal_point_char (){ const char *point; /* Determine it in a multithread-safe way. We know nl_langinfo is multithread-safe on glibc systems, but is not required to be multithread- safe by POSIX. sprintf(), however, is multithread-safe. localeconv() is rarely multithread-safe. */# if HAVE_NL_LANGINFO && __GLIBC__ point = nl_langinfo (RADIXCHAR);# elif 1 char pointbuf[5]; sprintf (pointbuf, "%#.0f", 1.0); point = &pointbuf[1];# else point = localeconv () -> decimal_point;# endif /* The decimal point is always a single byte: either '.' or ','. */ return (point[0] != '\0' ? point[0] : '.');}# endif#endif#if NEED_PRINTF_INFINITE_DOUBLE && !NEED_PRINTF_DOUBLE && !defined IN_LIBINTL/* Equivalent to !isfinite(x) || x == 0, but does not require libm. */static intis_infinite_or_zero (double x){ return isnand (x) || x + x == x;}#endif#if NEED_PRINTF_INFINITE_LONG_DOUBLE && !NEED_PRINTF_LONG_DOUBLE && !defined IN_LIBINTL/* Equivalent to !isfinite(x), but does not require libm. */static intis_infinitel (long double x){ return isnanl (x) || (x + x == x && x != 0.0L);}#endif#if (NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL/* Converting 'long double' to decimal without rare rounding bugs requires real bignums. We use the naming conventions of GNU gmp, but vastly simpler (and slower) algorithms. */typedef unsigned int mp_limb_t;# define GMP_LIMB_BITS 32typedef int mp_limb_verify[2 * (sizeof (mp_limb_t) * CHAR_BIT == GMP_LIMB_BITS) - 1];typedef unsigned long long mp_twolimb_t;# define GMP_TWOLIMB_BITS 64typedef int mp_twolimb_verify[2 * (sizeof (mp_twolimb_t) * CHAR_BIT == GMP_TWOLIMB_BITS) - 1];/* Representation of a bignum >= 0. */typedef struct{ size_t nlimbs; mp_limb_t *limbs; /* Bits in little-endian order, allocated with malloc(). */} mpn_t;/* Compute the product of two bignums >= 0. Return the allocated memory in case of success, NULL in case of memory allocation failure. */static void *multiply (mpn_t src1, mpn_t src2, mpn_t *dest){ const mp_limb_t *p1; const mp_limb_t *p2; size_t len1; size_t len2; if (src1.nlimbs <= src2.nlimbs) { len1 = src1.nlimbs; p1 = src1.limbs; len2 = src2.nlimbs; p2 = src2.limbs; } else { len1 = src2.nlimbs; p1 = src2.limbs; len2 = src1.nlimbs; p2 = src1.limbs; } /* Now 0 <= len1 <= len2. */ if (len1 == 0) { /* src1 or src2 is zero. */ dest->nlimbs = 0; dest->limbs = (mp_limb_t *) malloc (1); } else { /* Here 1 <= len1 <= len2. */ size_t dlen; mp_limb_t *dp; size_t k, i, j; dlen = len1 + len2; dp = (mp_limb_t *) malloc (dlen * sizeof (mp_limb_t)); if (dp == NULL) return NULL; for (k = len2; k > 0; ) dp[--k] = 0; for (i = 0; i < len1; i++) { mp_limb_t digit1 = p1[i]; mp_twolimb_t carry = 0; for (j = 0; j < len2; j++) { mp_limb_t digit2 = p2[j]; carry += (mp_twolimb_t) digit1 * (mp_twolimb_t) digit2; carry += dp[i + j]; dp[i + j] = (mp_limb_t) carry; carry = carry >> GMP_LIMB_BITS; } dp[i + len2] = (mp_limb_t) carry; } /* Normalise. */ while (dlen > 0 && dp[dlen - 1] == 0) dlen--; dest->nlimbs = dlen; dest->limbs = dp; } return dest->limbs;}/* Compute the quotient of a bignum a >= 0 and a bignum b > 0. a is written as a = q * b + r with 0 <= r < b. q is the quotient, r the remainder. Finally, round-to-even is performed: If r > b/2 or if r = b/2 and q is odd, q is incremented. Return the allocated memory in case of success, NULL in case of memory allocation failure. */static void *divide (mpn_t a, mpn_t b, mpn_t *q){ /* Algorithm: First normalise a and b: a=[a[m-1],...,a[0]], b=[b[n-1],...,b[0]] with m>=0 and n>0 (in base beta = 2^GMP_LIMB_BITS). If m<n, then q:=0 and r:=a. If m>=n=1, perform a single-precision division: r:=0, j:=m, while j>0 do {Here (q[m-1]*beta^(m-1)+...+q[j]*beta^j) * b[0] + r*beta^j = = a[m-1]*beta^(m-1)+...+a[j]*beta^j und 0<=r<b[0]<beta} j:=j-1, r:=r*beta+a[j], q[j]:=floor(r/b[0]), r:=r-b[0]*q[j]. Normalise [q[m-1],...,q[0]], yields q. If m>=n>1, perform a multiple-precision division: We have a/b < beta^(m-n+1). s:=intDsize-1-(hightest bit in b[n-1]), 0<=s<intDsize. Shift a and b left by s bits, copying them. r:=a. r=[r[m],...,r[0]], b=[b[n-1],...,b[0]] with b[n-1]>=beta/2. For j=m-n,...,0: {Here 0 <= r < b*beta^(j+1).} Compute q* : q* := floor((r[j+n]*beta+r[j+n-1])/b[n-1]). In case of overflow (q* >= beta) set q* := beta-1. Compute c2 := ((r[j+n]*beta+r[j+n-1]) - q* * b[n-1])*beta + r[j+n-2] and c3 := b[n-2] * q*. {We have 0 <= c2 < 2*beta^2, even 0 <= c2 < beta^2 if no overflow occurred. Furthermore 0 <= c3 < beta^2. If there was overflow and r[j+n]*beta+r[j+n-1] - q* * b[n-1] >= beta, i.e. c2 >= beta^2, the next test can be skipped.} While c3 > c2, {Here 0 <= c2 < c3 < beta^2} Put q* := q* - 1, c2 := c2 + b[n-1]*beta, c3 := c3 - b[n-2]. If q* > 0: Put r := r - b * q* * beta^j. In detail: [r[n+j],...,r[j]] := [r[n+j],...,r[j]] - q* * [b[n-1],...,b[0]]. hence: u:=0, for i:=0 to n-1 do u := u + q* * b[i], r[j+i]:=r[j+i]-(u mod beta) (+ beta, if carry), u:=u div beta (+ 1, if carry in subtraction) r[n+j]:=r[n+j]-u. {Since always u = (q* * [b[i-1],...,b[0]] div beta^i) + 1 < q* + 1 <= beta, the carry u does not overflow.} If a negative carry occurs, put q* := q* - 1 and [r[n+j],...,r[j]] := [r[n+j],...,r[j]] + [0,b[n-1],...,b[0]]. Set q[j] := q*. Normalise [q[m-n],..,q[0]]; this yields the quotient q. Shift [r[n-1],...,r[0]] right by s bits and normalise; this yields the rest r. The room for q[j] can be allocated at the memory location of r[n+j]. Finally, round-to-even: Shift r left by 1 bit. If r > b or if r = b and q[0] is odd, q := q+1. */ const mp_limb_t *a_ptr = a.limbs; size_t a_len = a.nlimbs; const mp_limb_t *b_ptr = b.limbs; size_t b_len = b.nlimbs; mp_limb_t *roomptr; mp_limb_t *tmp_roomptr = NULL; mp_limb_t *q_ptr; size_t q_len; mp_limb_t *r_ptr; size_t r_len; /* Allocate room for a_len+2 digits. (Need a_len+1 digits for the real division and 1 more digit for the final rounding of q.) */ roomptr = (mp_limb_t *) malloc ((a_len + 2) * sizeof (mp_limb_t)); if (roomptr == NULL) return NULL; /* Normalise a. */ while (a_len > 0 && a_ptr[a_len - 1] == 0) a_len--; /* Normalise b. */ for (;;) { if (b_len == 0) /* Division by zero. */ abort (); if (b_ptr[b_len - 1] == 0) b_len--; else break; } /* Here m = a_len >= 0 and n = b_len > 0. */ if (a_len < b_len) { /* m<n: trivial case. q=0, r := copy of a. */ r_ptr = roomptr; r_len = a_len; memcpy (r_ptr, a_ptr, a_len * sizeof (mp_limb_t)); q_ptr = roomptr + a_len; q_len = 0; } else if (b_len == 1) { /* n=1: single precision division. beta^(m-1) <= a < beta^m ==> beta^(m-2) <= a/b < beta^m */ r_ptr = roomptr; q_ptr = roomptr + 1; { mp_limb_t den = b_ptr[0]; mp_limb_t remainder = 0; const mp_limb_t *sourceptr = a_ptr + a_len; mp_limb_t *destptr = q_ptr + a_len; size_t count; for (count = a_len; count > 0; count--) { mp_twolimb_t num = ((mp_twolimb_t) remainder << GMP_LIMB_BITS) | *--sourceptr; *--destptr = num / den; remainder = num % den; } /* Normalise and store r. */ if (remainder > 0) { r_ptr[0] = remainder; r_len = 1; } else r_len = 0; /* Normalise q. */ q_len = a_len; if (q_ptr[q_len - 1] == 0) q_len--; } } else { /* n>1: multiple precision division. beta^(m-1) <= a < beta^m, beta^(n-1) <= b < beta^n ==> beta^(m-n-1) <= a/b < beta^(m-n+1). */ /* Determine s. */ size_t s; { mp_limb_t msd = b_ptr[b_len - 1]; /* = b[n-1], > 0 */ s = 31; if (msd >= 0x10000) { msd = msd >> 16; s -= 16; } if (msd >= 0x100) { msd = msd >> 8; s -= 8; } if (msd >= 0x10) { msd = msd >> 4; s -= 4; } if (msd >= 0x4) { msd = msd >> 2;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -