| /* mpfr_vasprintf -- main function for the printf functions family |
| plus helper macros & functions. |
| |
| Copyright 2007, 2008, 2009 Free Software Foundation, Inc. |
| Contributed by the Arenaire and Cacao projects, INRIA. |
| |
| This file is part of the GNU MPFR Library. |
| |
| The GNU MPFR 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 MPFR 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 MPFR Library; see the file COPYING.LIB. If not, write to |
| the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| /* The mpfr_printf-like functions are defined only if stdarg.h exists */ |
| #ifdef HAVE_STDARG |
| |
| #include <stdarg.h> |
| |
| #ifdef HAVE_WCHAR_H |
| #include <wchar.h> |
| #endif |
| |
| #if defined (__cplusplus) |
| #include <cstddef> |
| #define __STDC_LIMIT_MACROS /* SIZE_MAX defined with stdint.h inclusion */ |
| #else |
| #include <stddef.h> /* for ptrdiff_t */ |
| #endif |
| |
| #if HAVE_INTTYPES_H |
| # include <inttypes.h> /* for intmax_t */ |
| #else |
| # if HAVE_STDINT_H |
| # include <stdint.h> |
| # endif |
| #endif |
| |
| #include <string.h> /* for strlen, memcpy and others */ |
| |
| #include "mpfr-impl.h" |
| |
| /* Define a length modifier corresponding to mp_prec_t. |
| We use literal string instead of literal character so as to permit future |
| extension to long long int ("ll"). */ |
| #if _MPFR_PREC_FORMAT == 1 |
| #define MPFR_PREC_FORMAT_TYPE "h" |
| #define MPFR_PREC_FORMAT_SIZE 1 |
| #elif _MPFR_PREC_FORMAT == 2 |
| #define MPFR_PREC_FORMAT_TYPE "" |
| #define MPFR_PREC_FORMAT_SIZE 0 |
| #elif _MPFR_PREC_FORMAT == 3 |
| #define MPFR_PREC_FORMAT_TYPE "l" |
| #define MPFR_PREC_FORMAT_SIZE 1 |
| #else |
| #error "mpfr_prec_t size not supported" |
| #endif |
| |
| #if (__GMP_MP_SIZE_T_INT == 1) |
| #define MPFR_EXP_FORMAT_SPEC "i" |
| #elif (__GMP_MP_SIZE_T_INT == 0) |
| #define MPFR_EXP_FORMAT_SPEC "li" |
| #else |
| #error "mp_exp_t size not supported" |
| #endif |
| |
| /* Output for special values defined in the C99 standard */ |
| #define MPFR_NAN_STRING_LC "nan" |
| #define MPFR_NAN_STRING_UC "NAN" |
| #define MPFR_NAN_STRING_LENGTH 3 |
| #define MPFR_INF_STRING_LC "inf" |
| #define MPFR_INF_STRING_UC "INF" |
| #define MPFR_INF_STRING_LENGTH 3 |
| |
| /* The implicit \0 is useless, but we do not write num_to_text[16] |
| otherwise g++ complains. */ |
| static const char num_to_text[] = "0123456789abcdef"; |
| |
| /* some macro and functions for parsing format string */ |
| |
| /* Read an integer; saturate to INT_MAX. */ |
| #define READ_INT(ap, format, specinfo, field, label_out) \ |
| do { \ |
| while (*(format)) \ |
| { \ |
| int _i; \ |
| switch (*(format)) \ |
| { \ |
| case '0': \ |
| case '1': \ |
| case '2': \ |
| case '3': \ |
| case '4': \ |
| case '5': \ |
| case '6': \ |
| case '7': \ |
| case '8': \ |
| case '9': \ |
| specinfo.field = (specinfo.field <= INT_MAX / 10) ? \ |
| specinfo.field * 10 : INT_MAX; \ |
| _i = *(format) - '0'; \ |
| MPFR_ASSERTN (_i >= 0 && _i <= 9); \ |
| specinfo.field = (specinfo.field <= INT_MAX - _i) ? \ |
| specinfo.field + _i : INT_MAX; \ |
| ++(format); \ |
| break; \ |
| case '*': \ |
| specinfo.field = va_arg ((ap), int); \ |
| ++(format); \ |
| default: \ |
| goto label_out; \ |
| } \ |
| } \ |
| } while (0) |
| |
| /* arg_t contains all the types described by the 'type' field of the |
| format string */ |
| enum arg_t |
| { |
| NONE, |
| CHAR_ARG, |
| SHORT_ARG, |
| LONG_ARG, |
| LONG_LONG_ARG, |
| QUAD_ARG, |
| INTMAX_ARG, |
| SIZE_ARG, |
| PTRDIFF_ARG, |
| LONG_DOUBLE_ARG, |
| MPF_ARG, |
| MPQ_ARG, |
| MP_LIMB_ARG, |
| MP_LIMB_ARRAY_ARG, |
| MPZ_ARG, |
| MPFR_PREC_ARG, |
| MPFR_ARG, |
| UNSUPPORTED |
| }; |
| |
| /* Each conversion specification of the format string will be translated in a |
| printf_spec structure by the parser. |
| This structure is adapted from the GNU libc one. */ |
| struct printf_spec |
| { |
| unsigned int alt:1; /* # flag */ |
| unsigned int space:1; /* Space flag */ |
| unsigned int left:1; /* - flag */ |
| unsigned int showsign:1; /* + flag */ |
| unsigned int group:1; /* ' flag */ |
| |
| int width; /* Width */ |
| int prec; /* Precision */ |
| |
| enum arg_t arg_type; /* Type of argument */ |
| mp_rnd_t rnd_mode; /* Rounding mode */ |
| char spec; /* Conversion specifier */ |
| |
| char pad; /* Padding character */ |
| }; |
| |
| static void |
| specinfo_init (struct printf_spec *specinfo) |
| { |
| specinfo->alt = 0; |
| specinfo->space = 0; |
| specinfo->left = 0; |
| specinfo->showsign = 0; |
| specinfo->group = 0; |
| specinfo->width = 0; |
| specinfo->prec = 0; |
| specinfo->arg_type = NONE; |
| specinfo->rnd_mode = GMP_RNDN; |
| specinfo->spec = 'i'; |
| specinfo->pad = ' '; |
| } |
| |
| static const char * |
| parse_flags (const char *format, struct printf_spec *specinfo) |
| { |
| while (*format) |
| { |
| switch (*format) |
| { |
| case '0': |
| specinfo->pad = '0'; |
| ++format; |
| break; |
| case '#': |
| specinfo->alt = 1; |
| ++format; |
| break; |
| case '+': |
| specinfo->showsign = 1; |
| ++format; |
| break; |
| case ' ': |
| specinfo->space = 1; |
| ++format; |
| break; |
| case '-': |
| specinfo->left = 1; |
| ++format; |
| break; |
| case '\'': |
| /* Single UNIX Specification for thousand separator */ |
| specinfo->group = 1; |
| ++format; |
| break; |
| default: |
| return format; |
| } |
| } |
| return format; |
| } |
| |
| static const char * |
| parse_arg_type (const char *format, struct printf_spec *specinfo) |
| { |
| switch (*format) |
| { |
| case '\0': |
| break; |
| case 'h': |
| if (*++format == 'h') |
| #ifndef NPRINTF_HH |
| { |
| ++format; |
| specinfo->arg_type = CHAR_ARG; |
| } |
| #else |
| specinfo->arg_type = UNSUPPORTED; |
| #endif |
| else |
| specinfo->arg_type = SHORT_ARG; |
| break; |
| case 'l': |
| if (*++format == 'l') |
| { |
| ++format; |
| #if defined (HAVE_LONG_LONG) && !defined(NPRINTF_LL) |
| specinfo->arg_type = LONG_LONG_ARG; |
| #else |
| specinfo->arg_type = UNSUPPORTED; |
| #endif |
| break; |
| } |
| else |
| { |
| specinfo->arg_type = LONG_ARG; |
| break; |
| } |
| case 'j': |
| ++format; |
| #if defined(_MPFR_H_HAVE_INTMAX_T) && !defined(NPRINTF_J) |
| specinfo->arg_type = INTMAX_ARG; |
| #else |
| specinfo->arg_type = UNSUPPORTED; |
| #endif |
| break; |
| case 'z': |
| ++format; |
| specinfo->arg_type = SIZE_ARG; |
| break; |
| case 't': |
| ++format; |
| #ifndef NPRINTF_T |
| specinfo->arg_type = PTRDIFF_ARG; |
| #else |
| specinfo->arg_type = UNSUPPORTED; |
| #endif |
| break; |
| case 'L': |
| ++format; |
| #ifndef NPRINTF_L |
| specinfo->arg_type = LONG_DOUBLE_ARG; |
| #else |
| specinfo->arg_type = UNSUPPORTED; |
| #endif |
| break; |
| case 'F': |
| ++format; |
| specinfo->arg_type = MPF_ARG; |
| break; |
| case 'Q': |
| ++format; |
| specinfo->arg_type = MPQ_ARG; |
| break; |
| case 'M': |
| ++format; |
| /* The 'M' specifier was added in gmp 4.2.0 */ |
| specinfo->arg_type = MP_LIMB_ARG; |
| break; |
| case 'N': |
| ++format; |
| specinfo->arg_type = MP_LIMB_ARRAY_ARG; |
| break; |
| case 'Z': |
| ++format; |
| specinfo->arg_type = MPZ_ARG; |
| break; |
| |
| /* mpfr specific specifiers */ |
| case 'P': |
| ++format; |
| specinfo->arg_type = MPFR_PREC_ARG; |
| break; |
| case 'R': |
| ++format; |
| specinfo->arg_type = MPFR_ARG; |
| } |
| return format; |
| } |
| |
| |
| /* some macros and functions filling the buffer */ |
| |
| /* CONSUME_VA_ARG removes from va_list AP the type expected by SPECINFO */ |
| |
| /* With a C++ compiler wchar_t and enumeration in va_list are converted to |
| integer type : int, unsigned int, long or unsigned long (unfortunately, |
| this is implementation dependant). |
| We follow gmp which assumes in print/doprnt.c that wchar_t is converted |
| to int. */ |
| #ifdef HAVE_WCHAR_H |
| #define CASE_LONG_ARG(specinfo, ap) \ |
| case LONG_ARG: \ |
| if (((specinfo).spec == 'd') || ((specinfo).spec == 'i') \ |
| || ((specinfo).spec == 'o') || ((specinfo).spec == 'u') \ |
| || ((specinfo).spec == 'x') || ((specinfo).spec == 'X')) \ |
| (void) va_arg ((ap), long); \ |
| else if ((specinfo).spec == 'c') \ |
| (void) va_arg ((ap), wint_t); \ |
| else if ((specinfo).spec == 's') \ |
| (void) va_arg ((ap), int); /* we assume integer promotion */ \ |
| break; |
| #else |
| #define CASE_LONG_ARG(specinfo, ap) \ |
| case LONG_ARG: \ |
| (void) va_arg ((ap), long); \ |
| break; |
| #endif |
| |
| #if defined(_MPFR_H_HAVE_INTMAX_T) |
| #define CASE_INTMAX_ARG(specinfo, ap) \ |
| case INTMAX_ARG: \ |
| (void) va_arg ((ap), intmax_t); \ |
| break; |
| #else |
| #define CASE_INTMAX_ARG(specinfo, ap) |
| #endif |
| |
| #ifdef HAVE_LONG_LONG |
| #define CASE_LONG_LONG_ARG(specinfo, ap) \ |
| case LONG_LONG_ARG: \ |
| (void) va_arg ((ap), long long); \ |
| break; |
| #else |
| #define CASE_LONG_LONG_ARG(specinfo, ap) |
| #endif |
| |
| #define CONSUME_VA_ARG(specinfo, ap) \ |
| do { \ |
| switch ((specinfo).arg_type) \ |
| { \ |
| case CHAR_ARG: \ |
| case SHORT_ARG: \ |
| (void) va_arg ((ap), int); \ |
| break; \ |
| CASE_LONG_ARG (specinfo, ap) \ |
| CASE_LONG_LONG_ARG (specinfo, ap) \ |
| CASE_INTMAX_ARG (specinfo, ap) \ |
| case SIZE_ARG: \ |
| (void) va_arg ((ap), size_t); \ |
| break; \ |
| case PTRDIFF_ARG: \ |
| (void) va_arg ((ap), ptrdiff_t); \ |
| break; \ |
| case LONG_DOUBLE_ARG: \ |
| (void) va_arg ((ap), long double); \ |
| break; \ |
| case MPF_ARG: \ |
| (void) va_arg ((ap), mpf_srcptr); \ |
| break; \ |
| case MPQ_ARG: \ |
| (void) va_arg ((ap), mpq_srcptr); \ |
| break; \ |
| case MP_LIMB_ARG: \ |
| (void) va_arg ((ap), mp_ptr); \ |
| break; \ |
| case MP_LIMB_ARRAY_ARG: \ |
| (void) va_arg ((ap), mp_ptr); \ |
| (void) va_arg ((ap), mp_size_t); \ |
| break; \ |
| case MPZ_ARG: \ |
| (void) va_arg ((ap), mpz_srcptr); \ |
| break; \ |
| default: \ |
| switch ((specinfo).spec) \ |
| { \ |
| case 'd': \ |
| case 'i': \ |
| case 'o': \ |
| case 'u': \ |
| case 'x': \ |
| case 'X': \ |
| case 'c': \ |
| (void) va_arg ((ap), int); \ |
| break; \ |
| case 'f': \ |
| case 'F': \ |
| case 'e': \ |
| case 'E': \ |
| case 'g': \ |
| case 'G': \ |
| case 'a': \ |
| case 'A': \ |
| (void) va_arg ((ap), double); \ |
| break; \ |
| case 's': \ |
| (void) va_arg ((ap), char *); \ |
| break; \ |
| case 'p': \ |
| (void) va_arg ((ap), void *); \ |
| } \ |
| } \ |
| } while (0) |
| |
| /* process the format part which does not deal with mpfr types, |
| jump to external label 'error' if gmp_asprintf return -1. */ |
| #define FLUSH(flag, start, end, ap, buf_ptr) \ |
| do { \ |
| const size_t n = (end) - (start); \ |
| if ((flag)) \ |
| /* previous specifiers are understood by gmp_printf */ \ |
| { \ |
| MPFR_TMP_DECL (marker); \ |
| char *fmt_copy; \ |
| MPFR_TMP_MARK (marker); \ |
| fmt_copy = (char*) MPFR_TMP_ALLOC ((n + 1) * sizeof(char)); \ |
| strncpy (fmt_copy, (start), n); \ |
| fmt_copy[n] = '\0'; \ |
| if (sprntf_gmp ((buf_ptr), (fmt_copy), (ap)) == -1) \ |
| { \ |
| MPFR_TMP_FREE (marker); \ |
| goto error; \ |
| } \ |
| (flag) = 0; \ |
| MPFR_TMP_FREE (marker); \ |
| } \ |
| else if ((start) != (end)) \ |
| /* no conversion specification, just simple characters */ \ |
| buffer_cat ((buf_ptr), (start), n); \ |
| } while (0) |
| |
| struct string_buffer |
| { |
| char *start; /* beginning of the buffer */ |
| char *curr; /* last character (!= '\0') written */ |
| size_t size; /* buffer capacity */ |
| }; |
| |
| static void |
| buffer_init (struct string_buffer *b, size_t s) |
| { |
| b->start = (char *) (*__gmp_allocate_func) (s); |
| b->start[0] = '\0'; |
| b->curr = b->start; |
| b->size = s; |
| } |
| |
| /* Increase buffer size by a number of character being the least multiple of |
| 4096 greater than LEN+1. */ |
| static void |
| buffer_widen (struct string_buffer *b, size_t len) |
| { |
| const size_t pos = b->curr - b->start; |
| const size_t n = sizeof (char) * 4096 * (1 + len / 4096); |
| |
| b->start = |
| (char *) (*__gmp_reallocate_func) (b->start, b->size, b->size + n); |
| b->size += n; |
| b->curr = b->start + pos; |
| } |
| |
| /* Concatenate the LEN first characters of the string S to the buffer B and |
| expand it if needed. */ |
| static void |
| buffer_cat (struct string_buffer *b, const char *s, size_t len) |
| { |
| if (len == 0) |
| return; |
| |
| MPFR_ASSERTN (b->size < SIZE_MAX - len - 1); |
| MPFR_ASSERTD (len <= strlen (s)); |
| if (MPFR_UNLIKELY ((b->curr + len + 1) > (b->start + b->size))) |
| buffer_widen (b, len); |
| |
| strncat (b->curr, s, len); |
| b->curr += len; |
| } |
| |
| /* Add N characters C to the end of buffer B */ |
| static void |
| buffer_pad (struct string_buffer *b, const char c, const size_t n) |
| { |
| if (n == 0) |
| return; |
| |
| MPFR_ASSERTN (b->size < SIZE_MAX - n - 1); |
| if (MPFR_UNLIKELY ((b->curr + n + 1) > (b->start + b->size))) |
| buffer_widen (b, n); |
| |
| if (n == 1) |
| *b->curr = c; |
| else |
| memset (b->curr, c, n); |
| b->curr += n; |
| *b->curr = '\0'; |
| } |
| |
| /* Form a string by concatenating the first LEN characters of STR to TZ |
| zero(s), insert into one character C each 3 characters starting from end |
| to begining and concatenate the result to the buffer B. */ |
| static void |
| buffer_sandwich (struct string_buffer *b, char *str, size_t len, |
| const size_t tz, const char c) |
| { |
| const size_t step = 3; |
| const size_t size = len + tz; |
| const size_t r = size % step == 0 ? step : size % step; |
| const size_t q = size % step == 0 ? size / step - 1 : size / step; |
| size_t i; |
| |
| if (size == 0) |
| return; |
| if (c == '\0') |
| { |
| buffer_cat (b, str, len); |
| buffer_pad (b, '0', tz); |
| return; |
| } |
| |
| MPFR_ASSERTN (b->size < SIZE_MAX - size - 1 - q); |
| MPFR_ASSERTD (len <= strlen (str)); |
| if (MPFR_UNLIKELY ((b->curr + size + 1 + q) > (b->start + b->size))) |
| buffer_widen (b, size + q); |
| |
| /* first R significant digits */ |
| memcpy (b->curr, str, r); |
| b->curr += r; |
| str += r; |
| len -= r; |
| |
| /* blocks of thousands. Warning: STR might end in the middle of a block */ |
| for (i = 0; i < q; ++i) |
| { |
| *b->curr++ = c; |
| if (MPFR_LIKELY (len > 0)) |
| { |
| if (MPFR_LIKELY (len >= step)) |
| /* step significant digits */ |
| { |
| memcpy (b->curr, str, step); |
| len -= step; |
| } |
| else |
| /* last digits in STR, fill up thousand block with zeros */ |
| { |
| memcpy (b->curr, str, len); |
| memset (b->curr + len, '0', step - len); |
| len = 0; |
| } |
| } |
| else |
| /* trailing zeros */ |
| memset (b->curr, '0', step); |
| |
| b->curr += step; |
| str += step; |
| } |
| |
| *b->curr = '\0'; |
| } |
| |
| /* let gmp_xprintf process the part it can understand */ |
| static int |
| sprntf_gmp (struct string_buffer *b, const char *fmt, va_list ap) |
| { |
| int length; |
| char *s; |
| |
| length = gmp_vasprintf (&s, fmt, ap); |
| if (length > 0) |
| buffer_cat (b, s, length); |
| |
| mpfr_free_str (s); |
| return length; |
| } |
| |
| /* Helper struct and functions for temporary strings management */ |
| /* struct for easy string clearing */ |
| struct string_list |
| { |
| char *string; |
| struct string_list *next; /* NULL in last node */ |
| }; |
| |
| /* initialisation */ |
| static void |
| init_string_list (struct string_list *sl) |
| { |
| sl->string = NULL; |
| sl->next = NULL; |
| } |
| |
| /* clear all strings in the list */ |
| static void |
| clear_string_list (struct string_list *sl) |
| { |
| struct string_list *n; |
| |
| while (sl) |
| { |
| if (sl->string) |
| mpfr_free_str (sl->string); |
| n = sl->next; |
| (*__gmp_free_func) (sl, sizeof(struct string_list)); |
| sl = n; |
| } |
| } |
| |
| /* add a string in the list */ |
| static char * |
| register_string (struct string_list *sl, char *new_string) |
| { |
| /* look for the last node */ |
| while (sl->next) |
| sl = sl->next; |
| |
| sl->next = (struct string_list*) |
| (*__gmp_allocate_func) (sizeof (struct string_list)); |
| |
| sl = sl->next; |
| sl->next = NULL; |
| return sl->string = new_string; |
| } |
| |
| /* padding type: where are the padding characters */ |
| enum pad_t |
| { |
| LEFT, /* spaces in left hand side for right justification */ |
| LEADING_ZEROS, /* padding with '0' characters in integral part */ |
| RIGHT /* spaces in right hand side for left justification */ |
| }; |
| |
| /* number_parts details how much characters are needed in each part of a float |
| print. */ |
| struct number_parts |
| { |
| enum pad_t pad_type; /* Padding type */ |
| size_t pad_size; /* Number of padding characters */ |
| |
| char sign; /* Sign character */ |
| |
| char *prefix_ptr; /* Pointer to prefix part */ |
| size_t prefix_size; /* Number of characters in *prefix_ptr */ |
| |
| char thousands_sep; /* Thousands separator (only with style 'f') */ |
| |
| char *ip_ptr; /* Pointer to integral part characters*/ |
| size_t ip_size; /* Number of digits in *ip_ptr */ |
| int ip_trailing_zeros; /* Number of additional null digits in integral |
| part */ |
| |
| char point; /* Decimal point character */ |
| |
| int fp_leading_zeros; /* Number of additional leading zeros in fractional |
| part */ |
| char *fp_ptr; /* Pointer to fractional part characters */ |
| size_t fp_size; /* Number of digits in *fp_ptr */ |
| int fp_trailing_zeros; /* Number of additional trailing zeros in fractional |
| part */ |
| |
| char *exp_ptr; /* Pointer to exponent part */ |
| size_t exp_size; /* Number of characters in *exp_ptr */ |
| |
| struct string_list *sl; /* List of string buffers in use: we need such a |
| mechanism because fp_ptr may point into the same |
| string as ip_ptr */ |
| }; |
| |
| /* Determine the different parts of the string representation of the regular |
| number P when SPEC.SPEC is 'a', 'A', or 'b'. |
| |
| return -1 if some field > INT_MAX */ |
| static int |
| regular_ab (struct number_parts *np, mpfr_srcptr p, |
| const struct printf_spec spec) |
| { |
| int uppercase; |
| int base; |
| char *str; |
| mp_exp_t exp; |
| |
| uppercase = spec.spec == 'A'; |
| |
| /* sign */ |
| if (MPFR_IS_NEG (p)) |
| np->sign = '-'; |
| else if (spec.showsign || spec.space) |
| np->sign = spec.showsign ? '+' : ' '; |
| |
| if (spec.spec == 'a' || spec.spec == 'A') |
| /* prefix part */ |
| { |
| np->prefix_size = 2; |
| str = (char *) (*__gmp_allocate_func) (1 + np->prefix_size); |
| str[0] = '0'; |
| str[1] = uppercase ? 'X' : 'x'; |
| str[2] = '\0'; |
| np->prefix_ptr = register_string (np->sl, str); |
| } |
| |
| /* integral part */ |
| np->ip_size = 1; |
| base = (spec.spec == 'b') ? 2 : 16; |
| |
| if (spec.spec == 'b' || spec.prec != 0) |
| /* In order to avoid ambiguity in rounding to even case, we will always |
| output at least one fractional digit in binary mode */ |
| { |
| size_t nsd; |
| |
| /* Number of significant digits: |
| - if no given precision, let mpfr_get_str determine it; |
| - if a zero precision is specified and if we are in binary mode, then |
| ask for two binary digits, one before decimal point, and one after; |
| - if a non-zero precision is specified, then one digit before decimal |
| point plus SPEC.PREC after it. */ |
| nsd = spec.prec < 0 ? 0 |
| : (spec.prec == 0 && spec.spec == 'b') ? 2 : spec.prec + np->ip_size; |
| str = mpfr_get_str (0, &exp, base, nsd, p, spec.rnd_mode); |
| register_string (np->sl, str); |
| np->ip_ptr = MPFR_IS_NEG (p) ? ++str : str; /* skip sign if any */ |
| |
| if (base == 16) |
| /* EXP is the exponent for radix sixteen with decimal point BEFORE the |
| first digit, we want the exponent for radix two and the decimal |
| point AFTER the first digit. */ |
| { |
| MPFR_ASSERTN (exp > MPFR_EMIN_MIN /4); /* possible overflow */ |
| exp = (exp - 1) * 4; |
| } |
| else |
| /* EXP is the exponent for decimal point BEFORE the first digit, we |
| want the exponent for decimal point AFTER the first digit. */ |
| { |
| MPFR_ASSERTN (exp > MPFR_EMIN_MIN); /* possible overflow */ |
| --exp; |
| } |
| } |
| else |
| /* One hexadecimal digit is sufficient but mpfr_get_str returns at least |
| two digits when the base is a power of two. |
| So, in order to avoid double rounding, we will build our own string. */ |
| { |
| mp_limb_t *pm = MPFR_MANT (p); |
| mp_size_t ps; |
| int digit; |
| unsigned int shift; |
| int rnd_away; |
| |
| /* rnd_away: |
| 1 if round away from zero, |
| 0 if round to zero, |
| -1 if not decided yet. */ |
| rnd_away = |
| spec.rnd_mode == GMP_RNDD ? MPFR_IS_NEG (p) : |
| spec.rnd_mode == GMP_RNDU ? MPFR_IS_POS (p) : |
| spec.rnd_mode == GMP_RNDZ ? 0 : -1; |
| |
| /* exponent for radix-2 with the decimal point after the first |
| hexadecimal digit */ |
| MPFR_ASSERTN (MPFR_GET_EXP (p) > MPFR_EMIN_MIN + 3); /* possible |
| overflow */ |
| exp = MPFR_GET_EXP (p) - 4; |
| |
| /* Determine the radix-16 digit by grouping the 4 first digits. Even |
| if MPFR_PREC (p) < 4, we can read 4 bits in its first limb */ |
| shift = BITS_PER_MP_LIMB - 4; |
| ps = (MPFR_PREC (p) - 1) / BITS_PER_MP_LIMB; |
| digit = pm[ps] >> shift; |
| |
| if (MPFR_PREC (p) > 4) |
| /* round taking into account bits outside the first 4 ones */ |
| { |
| if (rnd_away == -1) |
| /* Round to nearest mode: we have to decide in that particular |
| case if we have to round away from zero or not */ |
| { |
| mp_limb_t limb, rb, mask; |
| |
| /* compute rounding bit */ |
| mask = MPFR_LIMB_ONE << (shift - 1); |
| rb = pm[ps] & mask; |
| if (rb == 0) |
| rnd_away = 0; |
| else |
| { |
| mask = MPFR_LIMB_MASK (shift - 1); |
| limb = pm[ps] & mask; |
| while ((ps > 0) && (limb == 0)) |
| limb = pm[--ps]; |
| if (limb == 0) |
| /* tie case, round to even */ |
| rnd_away = (digit & 1) ? 1 : 0; |
| else |
| rnd_away = 1; |
| } |
| } |
| |
| MPFR_ASSERTD (rnd_away >= 0); /* rounding direction is defined */ |
| if (rnd_away) |
| { |
| digit++; |
| if (digit > 15) |
| /* As we want only the first significant digit, we have |
| to shift one position to the left */ |
| { |
| digit >>= 1; |
| ++exp; /* no possible overflow because |
| exp == EXP(p)-3 */ |
| } |
| } |
| } |
| |
| MPFR_ASSERTD ((0 <= digit) && (digit <= 15)); |
| np->ip_size = 1; |
| str = (char *)(*__gmp_allocate_func) (1 + np->ip_size); |
| str[0] = num_to_text [digit]; |
| str[1] = '\0'; |
| |
| np->ip_ptr = register_string (np->sl, str); |
| } |
| |
| if (uppercase) |
| /* All digits in upper case */ |
| { |
| char *s1 = str; |
| while (*s1) |
| { |
| switch (*s1) |
| { |
| case 'a': |
| *s1 = 'A'; |
| break; |
| case 'b': |
| *s1 = 'B'; |
| break; |
| case 'c': |
| *s1 = 'C'; |
| break; |
| case 'd': |
| *s1 = 'D'; |
| break; |
| case 'e': |
| *s1 = 'E'; |
| break; |
| case 'f': |
| *s1 = 'F'; |
| break; |
| } |
| s1++; |
| } |
| } |
| |
| if (spec.spec == 'b' || spec.prec != 0) |
| /* compute the number of digits in fractional part */ |
| { |
| char *ptr; |
| size_t str_len; |
| |
| /* the sign has been skipped, skip also the first digit */ |
| ++str; |
| str_len = strlen (str); |
| ptr = str + str_len - 1; /* points to the end of str */ |
| |
| if (spec.prec < 0) |
| /* remove trailing zeros, if any */ |
| { |
| while ((*ptr == '0') && (str_len != 0)) |
| { |
| --ptr; |
| --str_len; |
| } |
| } |
| |
| if (str_len > INT_MAX) |
| /* too much digits in fractional part */ |
| return -1; |
| |
| if (str_len != 0) |
| /* there are some non-zero digits in fractional part */ |
| { |
| np->fp_ptr = str; |
| np->fp_size = str_len; |
| if ((int) str_len < spec.prec) |
| np->fp_trailing_zeros = spec.prec - str_len; |
| } |
| } |
| |
| /* decimal point */ |
| if ((np->fp_size != 0) || spec.alt) |
| np->point = MPFR_DECIMAL_POINT; |
| |
| /* the exponent part contains the character 'p', or 'P' plus the sign |
| character plus at least one digit and only as many more digits as |
| necessary to represent the exponent. |
| We assume that |EXP| < 10^INT_MAX. */ |
| np->exp_size = 3; |
| { |
| mp_exp_unsigned_t x; |
| |
| x = SAFE_ABS (mp_exp_unsigned_t, exp); |
| while (x > 9) |
| { |
| np->exp_size++; |
| x /= 10; |
| } |
| } |
| str = (char *) (*__gmp_allocate_func) (1 + np->exp_size); |
| np->exp_ptr = register_string (np->sl, str); |
| { |
| char exp_fmt[8]; /* contains at most 7 characters like in "p%+.1i", |
| or "P%+.2li" */ |
| |
| exp_fmt[0] = uppercase ? 'P' : 'p'; |
| exp_fmt[1] = '\0'; |
| strcat (exp_fmt, "%+.1" MPFR_EXP_FORMAT_SPEC); |
| |
| if (sprintf (str, exp_fmt, exp) < 0) |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* Determine the different parts of the string representation of the regular |
| number P when SPEC.SPEC is 'e', 'E', 'g', or 'G'. |
| |
| return -1 if some field > INT_MAX */ |
| static int |
| regular_eg (struct number_parts *np, mpfr_srcptr p, |
| const struct printf_spec spec) |
| { |
| char *str; |
| mp_exp_t exp; |
| |
| const int uppercase = spec.spec == 'E' || spec.spec == 'G'; |
| const int spec_g = spec.spec == 'g' || spec.spec == 'G'; |
| const int keep_trailing_zeros = (spec_g && spec.alt) |
| || (!spec_g && (spec.prec > 0)); |
| |
| /* sign */ |
| if (MPFR_IS_NEG (p)) |
| np->sign = '-'; |
| else if (spec.showsign || spec.space) |
| np->sign = spec.showsign ? '+' : ' '; |
| |
| /* integral part */ |
| np->ip_size = 1; |
| { |
| size_t nsd; |
| |
| /* Number of significant digits: |
| - if no given precision, then let mpfr_get_str determine it, |
| - if a precision is specified, then one digit before decimal point |
| plus SPEC.PREC after it. |
| We use the fact here that mpfr_get_exp allows us to ask for only one |
| significant digit when the base is not a power of 2. */ |
| nsd = (spec.prec < 0) ? 0 : spec.prec + np->ip_size; |
| str = mpfr_get_str (0, &exp, 10, nsd, p, spec.rnd_mode); |
| } |
| register_string (np->sl, str); |
| np->ip_ptr = MPFR_IS_NEG (p) ? ++str : str; /* skip sign if any */ |
| |
| if (spec.prec != 0) |
| /* compute the number of digits in fractional part */ |
| { |
| char *ptr; |
| size_t str_len; |
| |
| /* the sign has been skipped, skip also the first digit */ |
| ++str; |
| str_len = strlen (str); |
| ptr = str + str_len - 1; /* points to the end of str */ |
| |
| if (!keep_trailing_zeros) |
| /* remove trailing zeros, if any */ |
| { |
| while ((*ptr == '0') && (str_len != 0)) |
| { |
| --ptr; |
| --str_len; |
| } |
| } |
| |
| if (str_len > INT_MAX) |
| /* too much digits in fractional part */ |
| return -1; |
| |
| if (str_len != 0) |
| /* there are some non-zero digits in fractional part */ |
| { |
| np->fp_ptr = str; |
| np->fp_size = str_len; |
| if ((!spec_g || spec.alt) && (spec.prec > 0) |
| && ((int)str_len < spec.prec)) |
| /* add missing trailing zeros */ |
| np->fp_trailing_zeros = spec.prec - str_len; |
| } |
| } |
| |
| /* decimal point */ |
| if (np->fp_size != 0 || spec.alt) |
| np->point = MPFR_DECIMAL_POINT; |
| |
| /* EXP is the exponent for decimal point BEFORE the first digit, we want |
| the exponent for decimal point AFTER the first digit. |
| Here, no possible overflow because exp < MPFR_EXP (p) / 3 */ |
| exp--; |
| |
| /* the exponent part contains the character 'e', or 'E' plus the sign |
| character plus at least two digits and only as many more digits as |
| necessary to represent the exponent. |
| We assume that |EXP| < 10^INT_MAX. */ |
| np->exp_size = 3; |
| { |
| mp_exp_unsigned_t x; |
| |
| x = SAFE_ABS (mp_exp_unsigned_t, exp); |
| while (x > 9) |
| { |
| np->exp_size++; |
| x /= 10; |
| } |
| } |
| if (np->exp_size < 4) |
| np->exp_size = 4; |
| |
| str = (char *) (*__gmp_allocate_func) (1 + np->exp_size); |
| np->exp_ptr = register_string (np->sl, str); |
| |
| { |
| char exp_fmt[8]; /* e.g. "e%+.2i", or "E%+.2li" */ |
| |
| exp_fmt[0] = uppercase ? 'E' : 'e'; |
| exp_fmt[1] = '\0'; |
| strcat (exp_fmt, "%+.2" MPFR_EXP_FORMAT_SPEC); |
| |
| if (sprintf (str, exp_fmt, exp) < 0) |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* Determine the different parts of the string representation of the regular |
| number p when spec.spec is 'f', 'F', 'g', or 'G'. |
| |
| return -1 if some field of number_parts is greater than INT_MAX */ |
| static int |
| regular_fg (struct number_parts *np, mpfr_srcptr p, |
| const struct printf_spec spec) |
| { |
| mpfr_t x; |
| char * str; |
| const int spec_g = (spec.spec == 'g' || spec.spec == 'G'); |
| const int keep_trailing_zeros = spec_g && spec.alt; |
| |
| /* sign */ |
| if (MPFR_IS_NEG (p)) |
| np->sign = '-'; |
| else if (spec.showsign || spec.space) |
| np->sign = spec.showsign ? '+' : ' '; |
| |
| /* Determine the position of the most significant decimal digit. */ |
| { |
| /* Let p = m*10^e with 1 <= m < 10 and p = n*2^d with 0.5 <= d < 1. |
| We need at most 1+log2(floor(d/3)+1) bits of precision in order to |
| represent the exact value of e+1 if p >= 1, or |e| if p < 1. */ |
| mp_prec_t m; |
| mp_prec_t n; |
| |
| m = (mp_prec_t) SAFE_ABS (mp_exp_unsigned_t, MPFR_GET_EXP (p)); |
| m /= 3; |
| m++; |
| n = 1; |
| while (m != 0) |
| { |
| m >>= 1; |
| n++; |
| } |
| |
| if (n <= MPFR_PREC (p)) |
| mpfr_init2 (x, MPFR_PREC (p) + 1); |
| else |
| mpfr_init2 (x, n); |
| } |
| |
| if (MPFR_GET_EXP (p) <= 0) |
| /* 0 < p < 1 */ |
| { |
| int rnd_to_one; |
| |
| /* Is p round to +/-1 with rounding mode spec.rnd_mode and precision |
| spec.prec ? rnd_to_one: |
| 1 if |p| output as "1.00_0" |
| 0 if |p| output as "0.dd_d" |
| -1 if not decided yet */ |
| |
| if (spec_g || spec.prec >= 0) |
| { |
| mpfr_t y; |
| mpfr_t u; |
| |
| mpfr_init2 (u, MPFR_PREC (p)); |
| |
| /* compare y = |p| and 1 - 10^(-spec.prec) */ |
| MPFR_ALIAS (y, p, 1, MPFR_EXP (p)); |
| mpfr_set_si (u, -spec.prec, GMP_RNDN); /* FIXME: analyze error */ |
| mpfr_exp10 (u, u, GMP_RNDN); |
| mpfr_ui_sub (x, 1, u, GMP_RNDN); |
| |
| rnd_to_one = |
| mpfr_cmp (y, x) < 0 ? 0 : |
| spec.rnd_mode == GMP_RNDD ? MPFR_IS_NEG (p) : |
| spec.rnd_mode == GMP_RNDU ? MPFR_IS_POS (p) : |
| spec.rnd_mode == GMP_RNDZ ? 0 : -1; |
| |
| if (rnd_to_one == -1) |
| /* round to nearest mode */ |
| { |
| /* round to 1 iff y = |p| > 1 - 0.5 * 10^(-spec.prec) */ |
| mpfr_div_2ui (x, u, 1, GMP_RNDN); |
| mpfr_ui_sub (x, 1, x, GMP_RNDN); |
| |
| rnd_to_one = mpfr_cmp (y, x) > 0 ? 1 : 0; |
| } |
| mpfr_clear (u); |
| } |
| else |
| rnd_to_one = 0; |
| |
| MPFR_ASSERTD (rnd_to_one >= 0); /* rnd_to_one is defined */ |
| if (rnd_to_one) |
| /* one digit '1' in integral part */ |
| { |
| /* integral part */ |
| np->ip_size = 1; |
| str = (char *) (*__gmp_allocate_func) (1 + np->ip_size); |
| str[0] = '1'; |
| str[1] = '\0'; |
| np->ip_ptr = register_string (np->sl, str); |
| |
| if (spec.prec > 0) |
| /* fractional part */ |
| { |
| if (spec_g) |
| /* with specifier 'g', spec.prec is the number of |
| significant digits to display, take into account the digit |
| '1' in the integral part*/ |
| np->fp_trailing_zeros = spec.alt ? spec.prec - 1 : 0; |
| else |
| /* with specifier 'f', spec.prec is the number of digits |
| after the decimal point */ |
| np->fp_trailing_zeros = spec.prec; |
| } |
| } |
| else |
| /* one digit '0' in integral part */ |
| { |
| /* integral part */ |
| np->ip_size = 1; |
| str = (char *) (*__gmp_allocate_func) (1 + np->ip_size); |
| str[0] = '0'; |
| str[1] = '\0'; |
| np->ip_ptr = register_string (np->sl, str); |
| |
| if (spec.prec != 0) |
| /* fractional part */ |
| { |
| mpfr_t y; |
| |
| MPFR_ALIAS (y, p, 1, MPFR_EXP (p)); /* y = |p| */ |
| mpfr_log10 (x, y, GMP_RNDD); /* FIXME: analyze error */ |
| mpfr_floor (x, x); |
| mpfr_abs (x, x, GMP_RNDD); |
| /* We have rounded away from zero so that x == |e| (with |
| p = m*10^e, see above). */ |
| |
| if ((spec.prec > 0 && mpfr_cmp_si (x, spec.prec) > 0) |
| || (spec_g && mpfr_cmp_ui (x, 5) == 0)) |
| /* p is too small for the given precision, |
| output "0.0_00" or "0.0_01" depending on rnd_mode */ |
| { |
| int rnd_away; |
| |
| /* rnd_away: |
| 1 if round away from zero, |
| 0 if round to zero, |
| -1 if not decided yet. */ |
| rnd_away = |
| spec.rnd_mode == GMP_RNDD ? MPFR_IS_NEG (p) : |
| spec.rnd_mode == GMP_RNDU ? MPFR_IS_POS (p) : |
| spec.rnd_mode == GMP_RNDZ ? 0 : -1; |
| |
| if (rnd_away == -1) |
| /* round to nearest mode */ |
| { |
| /* round away iff |p| with x = 0.5 * 10^(-spec.prec) */ |
| mpfr_set_si (x, -spec.prec, GMP_RNDN); |
| mpfr_exp10 (x, x, GMP_RNDN); |
| mpfr_div_2ui (x, x, 1, GMP_RNDN); |
| |
| rnd_away = mpfr_cmp (y, x) > 0 ? 1 : 0; |
| } |
| |
| MPFR_ASSERTD (rnd_away >= 0); /* rounding direction is |
| defined */ |
| if (rnd_away) |
| /* the last output digit is '1' */ |
| { |
| if (spec_g) |
| /* |p| is output as 0.0001 */ |
| np->fp_leading_zeros = 3; |
| else |
| np->fp_leading_zeros = spec.prec - 1; |
| |
| np->fp_size = 1; |
| str = (char *) (*__gmp_allocate_func) (1 + np->fp_size); |
| str[0] = '1'; |
| str[1] = '\0'; |
| np->fp_ptr = register_string (np->sl, str); |
| } |
| else |
| /* only spec.prec zeros in fractional part */ |
| np->fp_leading_zeros = spec.prec; |
| } |
| else |
| /* some significant digits can be output in the fractional |
| part */ |
| { |
| mp_exp_t exp; |
| char *ptr; |
| size_t str_len; |
| const size_t nsd = spec.prec < 0 ? 0 |
| : spec.prec - mpfr_get_ui (x, GMP_RNDZ) + 1; |
| /* WARNING: nsd may equal 1, we use here the fact that |
| mpfr_get_str can return one digit with base ten |
| (undocumented feature, see comments in get_str.c) */ |
| |
| str = mpfr_get_str (NULL, &exp, 10, nsd, p, spec.rnd_mode); |
| register_string (np->sl, str); |
| np->fp_ptr = MPFR_IS_NEG (p) ? ++str : str; /* skip sign */ |
| np->fp_leading_zeros = exp < 0 ? -exp : 0; |
| |
| str_len = strlen (str); /* the sign has been skipped */ |
| ptr = str + str_len - 1; /* points to the end of str */ |
| |
| if (!keep_trailing_zeros) |
| /* remove trailing zeros, if any */ |
| { |
| while ((*ptr == '0') && str_len) |
| { |
| --ptr; |
| --str_len; |
| } |
| } |
| |
| if (str_len > INT_MAX) |
| /* too much digits in fractional part */ |
| { |
| mpfr_clear (x); |
| return -1; |
| } |
| MPFR_ASSERTD (str_len > 0); |
| np->fp_size = str_len; |
| |
| if (!spec_g && (spec.prec > 0) |
| && (np->fp_leading_zeros + np->fp_size < spec.prec)) |
| /* add missing trailing zeros */ |
| np->fp_trailing_zeros = spec.prec - np->fp_leading_zeros |
| - np->fp_size; |
| } |
| } |
| } |
| if (spec.alt || np->fp_leading_zeros != 0 || np->fp_size != 0 |
| || np->fp_trailing_zeros != 0) |
| np->point = MPFR_DECIMAL_POINT; |
| } |
| else |
| /* 1 <= p */ |
| { |
| mp_exp_t exp; |
| size_t nsd; /* Number of significant digits */ |
| |
| mpfr_abs (x, p, GMP_RNDD); /* With our choice of precision, |
| x == |p| exactly. */ |
| mpfr_log10 (x, x, GMP_RNDZ); |
| mpfr_floor (x, x); |
| mpfr_add_ui (x, x, 1, GMP_RNDZ); |
| /* We have rounded towards zero so that x == e + 1 (with p = m*10^e, |
| see above). x is now the number of digits in the integral part. */ |
| |
| MPFR_ASSERTD (mpfr_cmp_si (x, 0) >= 0); |
| if (mpfr_cmp_ui (x, INT_MAX) > 0) |
| /* P is too large to print all its integral part digits */ |
| { |
| mpfr_clear (x); |
| return -1; |
| } |
| |
| np->ip_size = mpfr_get_ui (x, GMP_RNDN); |
| |
| nsd = spec.prec < 0 ? 0 : spec.prec + np->ip_size; |
| str = mpfr_get_str (NULL, &exp, 10, nsd, p, spec.rnd_mode); |
| register_string (np->sl, str); |
| np->ip_ptr = MPFR_IS_NEG (p) ? ++str : str; /* skip sign */ |
| |
| if (spec.group) |
| /* thousands separator in integral part */ |
| np->thousands_sep = MPFR_THOUSANDS_SEPARATOR; |
| |
| if (nsd == 0 || (spec_g && !spec.alt)) |
| /* compute how much non-zero digits in integral and fractional |
| parts */ |
| { |
| size_t str_len; |
| str_len = strlen (str); /* note: the sign has been skipped */ |
| |
| if (np->ip_size > str_len) |
| /* mpfr_get_str doesn't give the trailing zeros when p is a |
| multiple of 10 (p integer, so no fractional part) */ |
| { |
| np->ip_trailing_zeros = np->ip_size - str_len; |
| np->ip_size = str_len; |
| if (spec.alt) |
| np->point = MPFR_DECIMAL_POINT; |
| } |
| else |
| /* str may contain some digits which are in fractional part */ |
| { |
| char *ptr; |
| |
| ptr = str + str_len - 1; /* points to the end of str */ |
| str_len -= np->ip_size; /* number of digits in fractional |
| part */ |
| |
| if (!keep_trailing_zeros) |
| /* remove trailing zeros, if any */ |
| { |
| while ((*ptr == '0') && (str_len != 0)) |
| { |
| --ptr; |
| --str_len; |
| } |
| } |
| |
| if (str_len > INT_MAX) |
| /* too much digits in fractional part */ |
| { |
| mpfr_clear (x); |
| return -1; |
| } |
| |
| if (str_len != 0) |
| /* some digits in fractional part */ |
| { |
| np->point = MPFR_DECIMAL_POINT; |
| np->fp_ptr = str + np->ip_size; |
| np->fp_size = str_len; |
| } |
| else if (spec.alt) |
| np->point = MPFR_DECIMAL_POINT; |
| } |
| } |
| else |
| /* spec.prec digits in fractional part */ |
| { |
| MPFR_ASSERTD (np->ip_size == exp); |
| |
| if (spec.prec != 0) |
| { |
| np->point = MPFR_DECIMAL_POINT; |
| np->fp_ptr = str + np->ip_size; |
| np->fp_size = spec.prec; |
| } |
| else if (spec.alt) |
| np->point = MPFR_DECIMAL_POINT; |
| } |
| } |
| |
| mpfr_clear (x); |
| return 0; |
| } |
| |
| /* partition_number determines the different parts of the string |
| representation of the number p according to the given specification. |
| partition_number initializes the given structure np, so all previous |
| information in that variable is lost. |
| return the total number of characters to be written. |
| return -1 if an error occured, in that case np's fields are in an undefined |
| state but all string buffers have been freed. */ |
| static int |
| partition_number (struct number_parts *np, mpfr_srcptr p, |
| struct printf_spec spec) |
| { |
| char *str; |
| long total; |
| int uppercase; |
| |
| /* WARNING: left justification means right space padding */ |
| np->pad_type = spec.left ? RIGHT : spec.pad == '0' ? LEADING_ZEROS : LEFT; |
| np->pad_size = 0; |
| np->sign = '\0'; |
| np->prefix_ptr =NULL; |
| np->prefix_size = 0; |
| np->thousands_sep = '\0'; |
| np->ip_ptr = NULL; |
| np->ip_size = 0; |
| np->ip_trailing_zeros = 0; |
| np->point = '\0'; |
| np->fp_leading_zeros = 0; |
| np->fp_ptr = NULL; |
| np->fp_size = 0; |
| np->fp_trailing_zeros = 0; |
| np->exp_ptr = NULL; |
| np->exp_size = 0; |
| np->sl = (struct string_list *) |
| (*__gmp_allocate_func) (sizeof (struct string_list)); |
| init_string_list (np->sl); |
| |
| uppercase = spec.spec == 'A' || spec.spec == 'E' || spec.spec == 'F' |
| || spec.spec == 'G'; |
| |
| if (MPFR_UNLIKELY (MPFR_IS_SINGULAR (p))) |
| { |
| if (MPFR_IS_NAN (p)) |
| { |
| if (np->pad_type == LEADING_ZEROS) |
| /* don't want "0000nan", change to right justification padding |
| with left spaces instead */ |
| np->pad_type = LEFT; |
| |
| if (uppercase) |
| { |
| np->ip_size = MPFR_NAN_STRING_LENGTH; |
| str = (char *) (*__gmp_allocate_func) (1 + np->ip_size); |
| strcpy (str, MPFR_NAN_STRING_UC); |
| np->ip_ptr = register_string (np->sl, str); |
| } |
| else |
| { |
| np->ip_size = MPFR_NAN_STRING_LENGTH; |
| str = (char *) (*__gmp_allocate_func) (1 + np->ip_size); |
| strcpy (str, MPFR_NAN_STRING_LC); |
| np->ip_ptr = register_string (np->sl, str); |
| } |
| } |
| else if (MPFR_IS_INF (p)) |
| { |
| if (np->pad_type == LEADING_ZEROS) |
| /* don't want "0000inf", change to right justification padding |
| with left spaces instead */ |
| np->pad_type = LEFT; |
| |
| if (MPFR_IS_NEG (p)) |
| np->sign = '-'; |
| |
| if (uppercase) |
| { |
| np->ip_size = MPFR_INF_STRING_LENGTH; |
| str = (char *) (*__gmp_allocate_func) (1 + np->ip_size); |
| strcpy (str, MPFR_INF_STRING_UC); |
| np->ip_ptr = register_string (np->sl, str); |
| } |
| else |
| { |
| np->ip_size = MPFR_INF_STRING_LENGTH; |
| str = (char *) (*__gmp_allocate_func) (1 + np->ip_size); |
| strcpy (str, MPFR_INF_STRING_LC); |
| np->ip_ptr = register_string (np->sl, str); |
| } |
| } |
| else |
| /* p == 0 */ |
| { |
| /* note: for 'g' spec, zero is always displayed with 'f'-style with |
| precision spec.prec - 1 and the trailing zeros are removed unless |
| the flag '#' is used. */ |
| if (MPFR_IS_NEG (p)) |
| /* signed zero */ |
| np->sign = '-'; |
| else if (spec.showsign || spec.space) |
| np->sign = spec.showsign ? '+' : ' '; |
| |
| if (spec.spec == 'a' || spec.spec == 'A') |
| /* prefix part */ |
| { |
| np->prefix_size = 2; |
| str = (char *) (*__gmp_allocate_func) (1 + np->prefix_size); |
| str[0] = '0'; |
| str[1] = uppercase ? 'X' : 'x'; |
| str[2] = '\0'; |
| np->prefix_ptr = register_string (np->sl, str); |
| } |
| |
| /* integral part */ |
| np->ip_size = 1; |
| str = (char *) (*__gmp_allocate_func) (1 + np->ip_size); |
| str[0] = '0'; |
| str[1] = '\0'; |
| np->ip_ptr = register_string (np->sl, str); |
| |
| if (spec.prec > 0 |
| && ((spec.spec != 'g' && spec.prec != 'G') || spec.alt)) |
| /* fractional part */ |
| { |
| np->point = MPFR_DECIMAL_POINT; |
| np->fp_trailing_zeros = (spec.spec == 'g' && spec.prec == 'G') ? |
| spec.prec - 1 : spec.prec; |
| } |
| else if (spec.alt) |
| np->point = MPFR_DECIMAL_POINT; |
| |
| if (spec.spec == 'a' || spec.spec == 'A' || spec.spec == 'b' |
| || spec.spec == 'e' || spec.spec == 'E') |
| /* exponent part */ |
| { |
| np->exp_size = (spec.spec == 'e' || spec.spec == 'E') ? 4 : 3; |
| str = (char *) (*__gmp_allocate_func) (1 + np->exp_size); |
| if (spec.spec == 'e' || spec.spec == 'E') |
| strcpy (str, uppercase ? "E+00" : "e+00"); |
| else |
| strcpy (str, uppercase ? "P+0" : "p+0"); |
| np->exp_ptr = register_string (np->sl, str); |
| } |
| } |
| } |
| else |
| /* regular p, p != 0 */ |
| { |
| if (spec.spec == 'a' || spec.spec == 'A' || spec.spec == 'b') |
| { |
| if (regular_ab (np, p, spec) == -1) |
| goto error; |
| } |
| else if (spec.spec == 'f' || spec.spec == 'F') |
| { |
| if (regular_fg (np, p, spec) == -1) |
| goto error; |
| } |
| else if (spec.spec == 'e' || spec.spec == 'E') |
| { |
| if (regular_eg (np, p, spec) == -1) |
| goto error; |
| } |
| else |
| /* %g case */ |
| { |
| /* Use the C99 rules: |
| if T > X >= -4 then the conversion is with style 'f'/'F' and |
| precision T-(X+1). |
| otherwise, the conversion is with style 'e'/'E' and |
| precision T-1. |
| where T is the threshold computed below and X is the exponent |
| that would be displayed with style 'e'. */ |
| int threshold; |
| long x; |
| mpfr_t y; |
| |
| MPFR_ALIAS (y, p, 1, MPFR_EXP (p)); /* y = |p| */ |
| |
| threshold = (spec.prec < 0) ? 6 : (spec.prec == 0) ? 1 : spec.prec; |
| { |
| mpfr_t z; |
| |
| mpfr_init2 (z, 53); |
| mpfr_log10 (z, y, GMP_RNDD); |
| x = mpfr_get_si (z, GMP_RNDD); |
| mpfr_clear (z); |
| } |
| |
| if (x < threshold && x >= -5) |
| { |
| if (x == -5) |
| /* |p| might be rounded to 1e-4 */ |
| { |
| int round_to_1em4; |
| |
| /* round_to_1em4: |
| 1 if |p| rounded to 1e-4, |
| 0 if not, |
| -1 if not decided yet. */ |
| round_to_1em4 = |
| spec.rnd_mode == GMP_RNDD ? MPFR_IS_NEG (p) : |
| spec.rnd_mode == GMP_RNDU ? MPFR_IS_POS (p) : |
| spec.rnd_mode == GMP_RNDZ ? 0 : -1; |
| |
| if (round_to_1em4 == -1) |
| /* round to nearest mode: |p| is output as "1e-04" iff |
| 0 < 10^(-4) - |p| <= 5 * 10^(-threshold-5) */ |
| { |
| mpfr_t z; |
| |
| mpfr_init2 (z, MPFR_PREC (p)); /* FIXME: analyse error*/ |
| mpfr_set_si (z, -threshold, GMP_RNDN); |
| mpfr_exp10 (z, z, GMP_RNDN); |
| mpfr_div_2ui (z, z, 1, GMP_RNDN); |
| mpfr_ui_sub (z, 1, z, GMP_RNDN); |
| /* here, z = 1 - 10^(-threshold)/2 */ |
| |
| mpfr_div_ui (z, z, 625, GMP_RNDN); |
| mpfr_div_2ui (z, z, 4, GMP_RNDN); |
| |
| round_to_1em4 = mpfr_cmp (y, z) < 0 ? 0 : 1; |
| mpfr_clear (z); |
| } |
| |
| MPFR_ASSERTD (round_to_1em4 >= 0); /* rounding is defined */ |
| if (round_to_1em4) |
| /* |p| = 0.0000abc_d is output as "1.00_0e-04" with |
| style 'e', so the conversion is with style 'f' */ |
| { |
| spec.prec = threshold + 3; |
| |
| if (regular_fg (np, p, spec) == -1) |
| goto error; |
| } |
| else |
| /* |p| = 0.0000abc_d is output as "a.bc_de-05" with |
| style 'e', so the conversion is with style 'e' */ |
| { |
| spec.prec = threshold - 1; |
| |
| if (regular_eg (np, p, spec) == -1) |
| goto error; |
| } |
| } |
| else |
| /* x >= -4, the conversion is with style 'f' */ |
| { |
| spec.prec = threshold - 1 - x; |
| |
| if (regular_fg (np, p, spec) == -1) |
| goto error; |
| } |
| } |
| else |
| { |
| spec.prec = threshold - 1; |
| |
| if (regular_eg (np, p, spec) == -1) |
| goto error; |
| } |
| } |
| } |
| |
| /* compute the number of characters to be written verifying it is not too |
| much */ |
| total = np->sign ? 1 : 0; |
| total += np->prefix_size; |
| total += np->ip_size; |
| if (MPFR_UNLIKELY (total < 0 || total > INT_MAX)) |
| goto error; |
| total += np->ip_trailing_zeros; |
| if (MPFR_UNLIKELY (total < 0 || total > INT_MAX)) |
| goto error; |
| if (np->thousands_sep) |
| /* ' flag, style f and the thousands separator in current locale is not |
| reduced to the null character */ |
| total += (np->ip_size + np->ip_trailing_zeros) / 3; |
| if (MPFR_UNLIKELY (total < 0 || total > INT_MAX)) |
| goto error; |
| if (np->point) |
| ++total; |
| total += np->fp_leading_zeros; |
| if (MPFR_UNLIKELY (total < 0 || total > INT_MAX)) |
| goto error; |
| total += np->fp_size; |
| if (MPFR_UNLIKELY (total < 0 || total > INT_MAX)) |
| goto error; |
| total += np->fp_trailing_zeros; |
| if (MPFR_UNLIKELY (total < 0 || total > INT_MAX)) |
| goto error; |
| total += np->exp_size; |
| if (MPFR_UNLIKELY (total < 0 || total > INT_MAX)) |
| goto error; |
| |
| if (spec.width > total) |
| /* pad with spaces or zeros depending on np->pad_type */ |
| { |
| np->pad_size = spec.width - total; |
| total += np->pad_size; /* here total == spec.width, |
| so 0 < total < INT_MAX */ |
| } |
| |
| return total; |
| |
| error: |
| clear_string_list (np->sl); |
| np->prefix_ptr = NULL; |
| np->ip_ptr = NULL; |
| np->fp_ptr = NULL; |
| np->exp_ptr = NULL; |
| return -1; |
| } |
| |
| /* sprnt_fp prints a mpfr_t according to spec.spec specification. |
| |
| return the size of the string (not counting the terminating '\0') |
| return -1 if the built string is too long (i.e. has more than |
| INT_MAX characters). */ |
| static int |
| sprnt_fp (struct string_buffer *buf, mpfr_srcptr p, |
| const struct printf_spec spec) |
| { |
| int length; |
| struct number_parts np; |
| |
| length = partition_number (&np, p, spec); |
| if (length < 0) |
| return -1; |
| |
| /* right justification padding with left spaces */ |
| if (np.pad_type == LEFT && np.pad_size != 0) |
| buffer_pad (buf, ' ', np.pad_size); |
| |
| /* sign character (may be '-', '+', or ' ') */ |
| if (np.sign) |
| buffer_pad (buf, np.sign, 1); |
| |
| /* prefix part */ |
| if (np.prefix_ptr) |
| buffer_cat (buf, np.prefix_ptr, np.prefix_size); |
| |
| /* right justification padding with leading zeros */ |
| if (np.pad_type == LEADING_ZEROS && np.pad_size != 0) |
| buffer_pad (buf, '0', np.pad_size); |
| |
| /* integral part (may also be "nan" or "inf") */ |
| MPFR_ASSERTN (np.ip_ptr != NULL); /* never empty */ |
| if (MPFR_UNLIKELY (np.thousands_sep)) |
| buffer_sandwich (buf, np.ip_ptr, np.ip_size, np.ip_trailing_zeros, |
| np.thousands_sep); |
| else |
| { |
| buffer_cat (buf, np.ip_ptr, np.ip_size); |
| |
| /* trailing zeros in integral part */ |
| if (np.ip_trailing_zeros != 0) |
| buffer_pad (buf, '0', np.ip_trailing_zeros); |
| } |
| |
| /* decimal point */ |
| if (np.point) |
| buffer_pad (buf, np.point, 1); |
| |
| /* leading zeros in fractional part */ |
| if (np.fp_leading_zeros != 0) |
| buffer_pad (buf, '0', np.fp_leading_zeros); |
| |
| /* significant digits in fractional part */ |
| if (np.fp_ptr) |
| buffer_cat (buf, np.fp_ptr, np.fp_size); |
| |
| /* trailing zeros in fractional part */ |
| if (np.fp_trailing_zeros != 0) |
| buffer_pad (buf, '0', np.fp_trailing_zeros); |
| |
| /* exponent part */ |
| if (np.exp_ptr) |
| buffer_cat (buf, np.exp_ptr, np.exp_size); |
| |
| /* left justication padding with right spaces */ |
| if (np.pad_type == RIGHT && np.pad_size != 0) |
| buffer_pad (buf, ' ', np.pad_size); |
| |
| clear_string_list (np.sl); |
| return length; |
| } |
| |
| int |
| mpfr_vasprintf (char **ptr, const char *fmt, va_list ap) |
| { |
| struct string_buffer buf; |
| size_t nbchar; |
| |
| /* informations on the conversion specification filled by the parser */ |
| struct printf_spec spec; |
| /* flag raised when previous part of fmt need to be processed by |
| gmp_vsnprintf */ |
| int gmp_fmt_flag; |
| /* beginning and end of the previous unprocessed part of fmt */ |
| const char *start, *end; |
| /* pointer to arguments for gmp_vasprintf */ |
| va_list ap2; |
| |
| MPFR_SAVE_EXPO_DECL (expo); |
| MPFR_SAVE_EXPO_MARK (expo); |
| |
| nbchar = 0; |
| buffer_init (&buf, 4096 * sizeof (char)); |
| gmp_fmt_flag = 0; |
| va_copy (ap2, ap); |
| start = fmt; |
| while (*fmt) |
| { |
| /* Look for the next format specification */ |
| while ((*fmt) && (*fmt != '%')) |
| ++fmt; |
| |
| if (*fmt == '\0') |
| break; |
| |
| if (*++fmt == '%') |
| /* %%: go one step further otherwise the second '%' would be |
| considered as a new conversion specification introducing |
| character */ |
| { |
| ++fmt; |
| continue; |
| } |
| |
| end = fmt - 1; |
| |
| /* format string analysis */ |
| specinfo_init (&spec); |
| fmt = parse_flags (fmt, &spec); |
| |
| READ_INT (ap, fmt, spec, width, width_analysis); |
| width_analysis: |
| if (spec.width < 0) |
| { |
| spec.left = 1; |
| spec.width = -spec.width; |
| MPFR_ASSERTN (spec.width < INT_MAX); |
| } |
| if (*fmt == '.') |
| { |
| const char *f = ++fmt; |
| READ_INT (ap, fmt, spec, prec, prec_analysis); |
| prec_analysis: |
| if (f == fmt) |
| spec.prec = -1; |
| } |
| else |
| spec.prec = -1; |
| |
| fmt = parse_arg_type (fmt, &spec); |
| if (spec.arg_type == UNSUPPORTED) |
| /* the current architecture doesn't support this type */ |
| { |
| goto error; |
| } |
| else if (spec.arg_type == MPFR_ARG) |
| { |
| switch (*fmt) |
| { |
| case '\0': |
| break; |
| case '*': |
| ++fmt; |
| spec.rnd_mode = (mpfr_rnd_t) va_arg (ap, int); |
| break; |
| case 'D': |
| ++fmt; |
| spec.rnd_mode = GMP_RNDD; |
| break; |
| case 'U': |
| ++fmt; |
| spec.rnd_mode = GMP_RNDU; |
| break; |
| case 'Z': |
| ++fmt; |
| spec.rnd_mode = GMP_RNDZ; |
| break; |
| case 'N': |
| ++fmt; |
| default: |
| spec.rnd_mode = GMP_RNDN; |
| } |
| } |
| |
| spec.spec = *fmt; |
| if (*fmt) |
| fmt++; |
| |
| /* Format processing */ |
| if (spec.spec == '\0') |
| /* end of the format string */ |
| break; |
| else if (spec.spec == 'n') |
| /* put the number of characters written so far in the location pointed |
| by the next va_list argument; the types of pointer accepted are the |
| same as in GMP (except unsupported quad_t) plus pointer to a mpfr_t |
| so as to be able to accept the same format strings. */ |
| { |
| void *p; |
| size_t nchar; |
| |
| p = va_arg (ap, void *); |
| FLUSH (gmp_fmt_flag, start, end, ap2, &buf); |
| va_end (ap2); |
| start = fmt; |
| nchar = buf.curr - buf.start; |
| |
| switch (spec.arg_type) |
| { |
| case CHAR_ARG: |
| *(char *) p = (char) nchar; |
| break; |
| case SHORT_ARG: |
| *(short *) p = (short) nchar; |
| break; |
| case LONG_ARG: |
| *(long *) p = (long) nchar; |
| break; |
| #ifdef HAVE_LONG_LONG |
| case LONG_LONG_ARG: |
| *(long long *) p = (long long) nchar; |
| break; |
| #endif |
| #ifdef _MPFR_H_HAVE_INTMAX_T |
| case INTMAX_ARG: |
| *(intmax_t *) p = (intmax_t) nchar; |
| break; |
| #endif |
| case SIZE_ARG: |
| *(size_t *) p = nchar; |
| break; |
| case PTRDIFF_ARG: |
| *(ptrdiff_t *) p = (ptrdiff_t) nchar; |
| break; |
| case MPF_ARG: |
| mpf_set_ui ((mpf_ptr) p, (unsigned long) nchar); |
| break; |
| case MPQ_ARG: |
| mpq_set_ui ((mpq_ptr) p, (unsigned long) nchar, 1L); |
| break; |
| case MP_LIMB_ARG: |
| *(mp_limb_t *) p = (mp_limb_t) nchar; |
| break; |
| case MP_LIMB_ARRAY_ARG: |
| { |
| mp_limb_t *q = (mp_limb_t *) p; |
| mp_size_t n; |
| n = va_arg (ap, mp_size_t); |
| if (n < 0) |
| n = -n; |
| else if (n == 0) |
| break; |
| |
| /* we assume here that mp_limb_t is wider than int */ |
| *q = (mp_limb_t) nchar; |
| while (--n != 0) |
| { |
| q++; |
| *q = (mp_limb_t) 0; |
| } |
| } |
| break; |
| case MPZ_ARG: |
| mpz_set_ui ((mpz_ptr) p, (unsigned long) nchar); |
| break; |
| |
| case MPFR_ARG: |
| mpfr_set_ui ((mpfr_ptr) p, (unsigned long) nchar, |
| spec.rnd_mode); |
| break; |
| |
| default: |
| *(int *) p = (int) nchar; |
| } |
| va_copy (ap2, ap); /* after the switch, due to MP_LIMB_ARRAY_ARG |
| case */ |
| } |
| else if (spec.arg_type == MPFR_PREC_ARG) |
| /* output mp_prec_t variable */ |
| { |
| char *s; |
| char format[MPFR_PREC_FORMAT_SIZE + 6]; /* see examples below */ |
| size_t length; |
| mpfr_prec_t prec; |
| prec = va_arg (ap, mpfr_prec_t); |
| |
| FLUSH (gmp_fmt_flag, start, end, ap2, &buf); |
| va_end (ap2); |
| va_copy (ap2, ap); |
| start = fmt; |
| |
| /* construct format string, like "%*.*hu" "%*.*u" or "%*.*lu" */ |
| format[0] = '%'; |
| format[1] = '*'; |
| format[2] = '.'; |
| format[3] = '*'; |
| format[4] = '\0'; |
| strcat (format, MPFR_PREC_FORMAT_TYPE); |
| format[4 + MPFR_PREC_FORMAT_SIZE] = spec.spec; |
| format[5 + MPFR_PREC_FORMAT_SIZE] = '\0'; |
| length = gmp_asprintf (&s, format, spec.width, spec.prec, prec); |
| if (buf.size <= INT_MAX - length) |
| { |
| buffer_cat (&buf, s, length); |
| mpfr_free_str (s); |
| } |
| else |
| { |
| mpfr_free_str (s); |
| goto overflow_error; |
| } |
| } |
| else if (spec.arg_type == MPFR_ARG) |
| /* output a mpfr_t variable */ |
| { |
| mpfr_srcptr p; |
| |
| p = va_arg (ap, mpfr_srcptr); |
| |
| FLUSH (gmp_fmt_flag, start, end, ap2, &buf); |
| va_end (ap2); |
| va_copy (ap2, ap); |
| start = fmt; |
| |
| switch (spec.spec) |
| { |
| case 'a': |
| case 'A': |
| case 'b': |
| case 'e': |
| case 'E': |
| case 'f': |
| case 'F': |
| case 'g': |
| case 'G': |
| if (sprnt_fp (&buf, p, spec) < 0) |
| goto overflow_error; |
| break; |
| |
| default: |
| /* unsupported specifier */ |
| goto error; |
| } |
| } |
| else |
| /* gmp_printf specification, step forward in the va_list */ |
| { |
| CONSUME_VA_ARG (spec, ap); |
| gmp_fmt_flag = 1; |
| } |
| } |
| |
| if (start != fmt) |
| FLUSH (gmp_fmt_flag, start, fmt, ap2, &buf); |
| |
| va_end (ap2); |
| nbchar = buf.curr - buf.start; |
| MPFR_ASSERTD (nbchar == strlen (buf.start)); |
| buf.start = |
| (char *) (*__gmp_reallocate_func) (buf.start, buf.size, nbchar + 1); |
| *ptr = buf.start; |
| |
| /* If nbchar is larger than INT_MAX, the ISO C99 standard is silent, but |
| POSIX says concerning the snprintf() function: |
| "[EOVERFLOW] The value of n is greater than {INT_MAX} or the |
| number of bytes needed to hold the output excluding the |
| terminating null is greater than {INT_MAX}." See: |
| http://www.opengroup.org/onlinepubs/009695399/functions/fprintf.html |
| But it doesn't say anything concerning the other printf-like functions. |
| A defect report has been submitted to austin-review-l (item 2532). |
| So, for the time being, we return a negative value and set the erange |
| flag, and set errno to EOVERFLOW in POSIX system. */ |
| if (nbchar <= INT_MAX) |
| { |
| MPFR_SAVE_EXPO_FREE (expo); |
| return nbchar; |
| } |
| |
| overflow_error: |
| MPFR_SAVE_EXPO_UPDATE_FLAGS(expo, MPFR_FLAGS_ERANGE); |
| #ifdef EOVERFLOW |
| errno = EOVERFLOW; |
| #endif |
| |
| error: |
| MPFR_SAVE_EXPO_FREE (expo); |
| *ptr = NULL; |
| (*__gmp_free_func) (buf.start, buf.size); |
| |
| return -1; |
| } |
| |
| #endif /* HAVE_STDARG */ |