efee5258bc
The MPFR library is a C library for multiple-precision floating-point computations with exact rounding (also called correct rounding). It is based on the GMP multiple-precision library and should replace the MPF class in further releases of GMP. GCC >= 4.2 requires MPFR.
2206 lines
66 KiB
C
2206 lines
66 KiB
C
/* mpfr_vasprintf -- main function for the printf functions family
|
|
plus helper macros & functions.
|
|
|
|
Copyright 2007, 2008, 2009, 2010, 2011 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 3 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.LESSER. If not, see
|
|
http://www.gnu.org/licenses/ or 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>
|
|
|
|
#ifndef HAVE_VA_COPY
|
|
# ifdef HAVE___VA_COPY
|
|
# define va_copy(dst,src) __va_copy(dst, src)
|
|
# else
|
|
/* autoconf manual advocates this fallback.
|
|
This is also the solution chosen by gmp */
|
|
# define va_copy(dst,src) \
|
|
do { memcpy(&(dst), &(src), sizeof(va_list)); } while (0)
|
|
# endif /* HAVE___VA_COPY */
|
|
#endif /* HAVE_VA_COPY */
|
|
|
|
#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> /* for WINT_MAX in particular */
|
|
# endif
|
|
#endif
|
|
|
|
#define MPFR_NEED_LONGLONG_H
|
|
#include "mpfr-impl.h"
|
|
|
|
/* Define a length modifier corresponding to mpfr_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 "mpfr_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,
|
|
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 */
|
|
mpfr_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 = MPFR_RNDN;
|
|
specinfo->spec = '\0';
|
|
specinfo->pad = ' ';
|
|
}
|
|
|
|
#define FLOATING_POINT_ARG_TYPE(at) \
|
|
((at) == MPFR_ARG || (at) == MPF_ARG || (at) == LONG_DOUBLE_ARG)
|
|
|
|
#define INTEGER_LIKE_ARG_TYPE(at) \
|
|
((at) == SHORT_ARG || (at) == LONG_ARG || (at) == LONG_LONG_ARG \
|
|
|| (at) == INTMAX_ARG || (at) == MPFR_PREC_ARG || (at) == MPZ_ARG \
|
|
|| (at) == MPQ_ARG || (at) == MP_LIMB_ARG || (at) == MP_LIMB_ARRAY_ARG \
|
|
|| (at) == CHAR_ARG || (at) == SIZE_ARG || (at) == PTRDIFF_ARG)
|
|
|
|
static int
|
|
specinfo_is_valid (struct printf_spec spec)
|
|
{
|
|
switch (spec.spec)
|
|
{
|
|
case 'n':
|
|
return -1;
|
|
|
|
case 'a': case 'A':
|
|
case 'e': case 'E':
|
|
case 'f': case 'F':
|
|
case 'g': case 'G':
|
|
return (spec.arg_type == NONE
|
|
|| FLOATING_POINT_ARG_TYPE (spec.arg_type));
|
|
|
|
case 'b':
|
|
return spec.arg_type == MPFR_ARG;
|
|
|
|
case 'd': case 'i':
|
|
case 'u': case 'o':
|
|
case 'x': case 'X':
|
|
return (spec.arg_type == NONE
|
|
|| INTEGER_LIKE_ARG_TYPE (spec.arg_type));
|
|
|
|
case 'c':
|
|
case 's':
|
|
return (spec.arg_type == NONE || spec.arg_type == LONG_ARG);
|
|
|
|
case 'p':
|
|
return spec.arg_type == NONE;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
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 (because wchar_t <= int).
|
|
For wint_t, we assume that the case WINT_MAX < INT_MAX yields an
|
|
integer promotion. */
|
|
#ifdef HAVE_WCHAR_H
|
|
#if defined(WINT_MAX) && WINT_MAX < INT_MAX
|
|
typedef int mpfr_va_wint; /* integer promotion */
|
|
#else
|
|
typedef wint_t mpfr_va_wint;
|
|
#endif
|
|
#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), mpfr_va_wint); \
|
|
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_limb_t); \
|
|
break; \
|
|
case MP_LIMB_ARRAY_ARG: \
|
|
(void) va_arg ((ap), mpfr_limb_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); \
|
|
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; /* null terminating character */
|
|
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 = 0x1000 + (len & ~((size_t) 0xfff));
|
|
MPFR_ASSERTD (pos < b->size);
|
|
|
|
MPFR_ASSERTN ((len & ~((size_t) 4095)) <= (size_t)(SIZE_MAX - 4096));
|
|
MPFR_ASSERTN (b->size < SIZE_MAX - n);
|
|
|
|
b->start =
|
|
(char *) (*__gmp_reallocate_func) (b->start, b->size, b->size + n);
|
|
b->size += n;
|
|
b->curr = b->start + pos;
|
|
|
|
MPFR_ASSERTD (pos < b->size);
|
|
MPFR_ASSERTD (*b->curr == '\0');
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
MPFR_ASSERTD (len != 0);
|
|
MPFR_ASSERTD (len <= strlen (s));
|
|
|
|
if (MPFR_UNLIKELY ((b->curr + len) >= (b->start + b->size)))
|
|
buffer_widen (b, len);
|
|
|
|
strncat (b->curr, s, len);
|
|
b->curr += len;
|
|
|
|
MPFR_ASSERTD (b->curr < b->start + b->size);
|
|
MPFR_ASSERTD (*b->curr == '\0');
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
MPFR_ASSERTD (n != 0);
|
|
|
|
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';
|
|
|
|
MPFR_ASSERTD (b->curr < b->start + b->size);
|
|
}
|
|
|
|
/* 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;
|
|
|
|
MPFR_ASSERTD (size != 0);
|
|
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';
|
|
|
|
MPFR_ASSERTD (b->curr < b->start + b->size);
|
|
}
|
|
|
|
/* 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 */
|
|
};
|
|
|
|
/* For a real non zero number x, what is the base exponent f when rounding x
|
|
with rounding mode r to r(x) = m*b^f, where m is a digit and 1 <= m < b ?
|
|
Return non zero value if x is rounded up to b^f, return zero otherwise */
|
|
static int
|
|
next_base_power_p (mpfr_srcptr x, int base, mpfr_rnd_t rnd)
|
|
{
|
|
mpfr_prec_t nbits;
|
|
mp_limb_t pm;
|
|
mp_limb_t xm;
|
|
|
|
MPFR_ASSERTD (MPFR_IS_PURE_FP (x));
|
|
MPFR_ASSERTD (base == 2 || base == 16);
|
|
|
|
/* Warning: the decimal point is AFTER THE FIRST DIGIT in this output
|
|
representation. */
|
|
nbits = base == 2 ? 1 : 4;
|
|
|
|
if (rnd == MPFR_RNDZ
|
|
|| (rnd == MPFR_RNDD && MPFR_IS_POS (x))
|
|
|| (rnd == MPFR_RNDU && MPFR_IS_NEG (x))
|
|
|| MPFR_PREC (x) <= nbits)
|
|
/* no rounding when printing x with 1 digit */
|
|
return 0;
|
|
|
|
xm = MPFR_MANT (x) [MPFR_LIMB_SIZE (x) - 1];
|
|
pm = MPFR_LIMB_MASK (GMP_NUMB_BITS - nbits);
|
|
if ((xm & ~pm) ^ ~pm)
|
|
/* do no round up if some of the nbits first bits are 0s. */
|
|
return 0;
|
|
|
|
if (rnd == MPFR_RNDN)
|
|
/* mask for rounding bit */
|
|
pm = (MPFR_LIMB_ONE << (GMP_NUMB_BITS - nbits - 1));
|
|
|
|
/* round up if some remaining bits are 1 */
|
|
/* warning: the return value must be an int */
|
|
return xm & pm ? 1 : 0;
|
|
}
|
|
|
|
/* For a real non zero number x, what is the exponent f when rounding x with
|
|
rounding mode r to r(x) = m*10^f, where m has p+1 digits and 1 <= m < 10 ?
|
|
|
|
Return +1 if x is rounded up to 10^f, return zero otherwise.
|
|
If e is not NULL, *e is set to f. */
|
|
static int
|
|
round_to_10_power (mpfr_exp_t *e, mpfr_srcptr x, mpfr_prec_t p, mpfr_rnd_t r)
|
|
{
|
|
mpfr_t f, u, v, y;
|
|
mpfr_prec_t m;
|
|
mpfr_exp_t ex;
|
|
mpfr_uexp_t uexp;
|
|
int roundup = -1; /* boolean (-1: not set) */
|
|
|
|
MPFR_ZIV_DECL (loop);
|
|
|
|
/* y = abs(x) */
|
|
MPFR_ALIAS (y, x, 1, MPFR_EXP(x));
|
|
|
|
/* we want f = floor(log(|x|)/log(10)) exactly.
|
|
we have |f| >= |Exp(x)|/3,
|
|
then m = ceil(log(uexp/3)/log(2)) > log(f)/log(2)
|
|
is a sufficient precision for f. */
|
|
ex = mpfr_get_exp (x);
|
|
uexp = SAFE_ABS (mpfr_uexp_t, ex) / 3;
|
|
m = 1;
|
|
while (uexp)
|
|
{
|
|
uexp >>= 1;
|
|
m++;
|
|
}
|
|
if (m < 2)
|
|
m = 2;
|
|
mpfr_init2 (f, m);
|
|
mpfr_log10 (f, y, MPFR_RNDD);
|
|
mpfr_floor (f, f);
|
|
|
|
/* In most cases, the output exponent is f. */
|
|
if (e != NULL)
|
|
*e = (mpfr_exp_t)mpfr_get_si (f, MPFR_RNDD);
|
|
|
|
if (r == MPFR_RNDZ
|
|
|| (MPFR_IS_POS (x) && r == MPFR_RNDD)
|
|
|| (MPFR_IS_NEG (x) && r == MPFR_RNDU))
|
|
/* If rounding toward zero, the exponent is f */
|
|
{
|
|
mpfr_clear (f);
|
|
return 0;
|
|
}
|
|
|
|
/* Is |x| less than 10^(f+1) - 10^(f-p)? */
|
|
{
|
|
int cmp;
|
|
int inex_u, inex_v, inex_w;
|
|
mpfr_exp_t exp_u, exp_v, exp_w;
|
|
|
|
m = MPFR_PREC (x);
|
|
m += MPFR_INT_CEIL_LOG2 (m);
|
|
mpfr_init2 (u, m);
|
|
mpfr_init2 (v, m);
|
|
|
|
MPFR_ZIV_INIT (loop, m);
|
|
for (;;)
|
|
{
|
|
mpfr_set_prec (u, m);
|
|
mpfr_set_prec (v, m);
|
|
|
|
/* u = o(10^(f+1)) rounding toward -infinity
|
|
error (u) < 1 ulp(u)
|
|
error(u) = 0 if inex_u = 0 */
|
|
mpfr_add_ui (u, f, 1, MPFR_RNDN);
|
|
inex_u = mpfr_ui_pow (u, 10, u, MPFR_RNDD);
|
|
exp_u = MPFR_EXP (u);
|
|
|
|
/* if r = rounding to nearest
|
|
v = o(0.5 * 10^(f-p)) rounding toward +infinity
|
|
else
|
|
v = o(10^(f-p)) rounding toward +infinity
|
|
|
|
error(v) < 1 ulp(v)
|
|
error(v) = 0 if inex_v = 0 */
|
|
mpfr_sub_ui (v, f, p, MPFR_RNDN);
|
|
inex_v = mpfr_ui_pow (v, 10, v, MPFR_RNDU);
|
|
if (r == MPFR_RNDN)
|
|
mpfr_div_2ui (v, v, 1, MPFR_RNDN);
|
|
exp_v = MPFR_EXP (v);
|
|
|
|
/* w = o(u-v) rounding toward -infinity
|
|
w is an approximation of 10^(f+1) - v with
|
|
error(w) < 1 ulp(w) + error(u) + error(v)
|
|
error(w) = 0 iff inex_u = inex_v = inex_diff = 0 */
|
|
inex_w = mpfr_sub (u, u, v, MPFR_RNDD);
|
|
exp_w = MPFR_EXP (u);
|
|
|
|
cmp = mpfr_cmp (y, u);
|
|
|
|
if (cmp < 0)
|
|
/* |x| < u <= 10^(f+1) - v, the exponent is f */
|
|
{
|
|
roundup = 0;
|
|
break;
|
|
}
|
|
else if (cmp == 0 && inex_u == 0 && inex_v == 0 && inex_w == 0)
|
|
/* |x| = u = 10^(f+1) - v, the exponent is f+1 */
|
|
{
|
|
if (e != NULL)
|
|
(*e)++;
|
|
|
|
roundup = +1;
|
|
break;
|
|
}
|
|
|
|
/* compare |x| with w + error(w) */
|
|
if (inex_u)
|
|
mpfr_set_ui_2exp (v, 1, exp_u - m, MPFR_RNDU);
|
|
else
|
|
mpfr_set_ui (v, 0, MPFR_RNDN);
|
|
if (inex_v)
|
|
mpfr_set_ui_2exp (v, 1, exp_v - m, MPFR_RNDU);
|
|
if (inex_w)
|
|
mpfr_set_ui_2exp (v, 1, exp_w - m, MPFR_RNDU);
|
|
|
|
mpfr_add (u, u, v, MPFR_RNDU);
|
|
if (mpfr_cmp (y, u) >= 0)
|
|
{
|
|
if (e != NULL)
|
|
*e = (mpfr_exp_t)mpfr_get_si (f, MPFR_RNDD) + 1;
|
|
|
|
roundup = +1;
|
|
break;
|
|
}
|
|
|
|
MPFR_ZIV_NEXT (loop, m);
|
|
}
|
|
MPFR_ZIV_FREE (loop);
|
|
mpfr_clear (u);
|
|
mpfr_clear (v);
|
|
}
|
|
|
|
MPFR_ASSERTD (roundup != -1);
|
|
mpfr_clear (f);
|
|
return roundup;
|
|
}
|
|
|
|
/* 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;
|
|
mpfr_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.prec != 0)
|
|
{
|
|
size_t nsd;
|
|
|
|
/* Number of significant digits:
|
|
- if no given precision, let mpfr_get_str determine it;
|
|
- 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 + 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 if (next_base_power_p (p, base, spec.rnd_mode))
|
|
{
|
|
str = (char *)(*__gmp_allocate_func) (2);
|
|
str[0] = '1';
|
|
str[1] = '\0';
|
|
np->ip_ptr = register_string (np->sl, str);
|
|
|
|
exp = MPFR_GET_EXP (p);
|
|
}
|
|
else if (base == 2)
|
|
{
|
|
str = (char *)(*__gmp_allocate_func) (2);
|
|
str[0] = '1';
|
|
str[1] = '\0';
|
|
np->ip_ptr = register_string (np->sl, str);
|
|
|
|
exp = MPFR_GET_EXP (p) - 1;
|
|
}
|
|
else
|
|
{
|
|
int digit;
|
|
mp_limb_t msl = MPFR_MANT (p)[MPFR_LIMB_SIZE (p) - 1];
|
|
int rnd_bit = GMP_NUMB_BITS - 5;
|
|
|
|
/* pick up the 4 first bits */
|
|
digit = msl >> (rnd_bit+1);
|
|
if (spec.rnd_mode == MPFR_RNDA
|
|
|| (spec.rnd_mode == MPFR_RNDU && MPFR_IS_POS (p))
|
|
|| (spec.rnd_mode == MPFR_RNDD && MPFR_IS_NEG (p))
|
|
|| (spec.rnd_mode == MPFR_RNDN
|
|
&& (msl & (MPFR_LIMB_ONE << rnd_bit))))
|
|
digit++;
|
|
MPFR_ASSERTD ((0 <= digit) && (digit <= 15));
|
|
|
|
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);
|
|
|
|
exp = MPFR_GET_EXP (p) - 4;
|
|
}
|
|
|
|
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 many 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;
|
|
{
|
|
mpfr_uexp_t x;
|
|
|
|
x = SAFE_ABS (mpfr_uexp_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;
|
|
mpfr_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_str 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 many 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;
|
|
{
|
|
mpfr_uexp_t x;
|
|
|
|
x = SAFE_ABS (mpfr_uexp_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_exp_t exp;
|
|
char * str;
|
|
const int spec_g = (spec.spec == 'g' || spec.spec == 'G');
|
|
const int keep_trailing_zeros = spec_g && spec.alt;
|
|
|
|
/* WARNING: an empty precision field is forbidden (it means precision = 6
|
|
and it should have been changed to 6 before the function call) */
|
|
MPFR_ASSERTD (spec.prec >= 0);
|
|
|
|
/* sign */
|
|
if (MPFR_IS_NEG (p))
|
|
np->sign = '-';
|
|
else if (spec.showsign || spec.space)
|
|
np->sign = spec.showsign ? '+' : ' ';
|
|
|
|
if (MPFR_GET_EXP (p) <= 0)
|
|
/* 0 < |p| < 1 */
|
|
{
|
|
/* Most of the time, integral part is 0 */
|
|
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)
|
|
/* only two possibilities: either 1 or 0. */
|
|
{
|
|
mpfr_t y;
|
|
/* y = abs(p) */
|
|
MPFR_ALIAS (y, p, 1, MPFR_EXP (p));
|
|
|
|
if (spec.rnd_mode == MPFR_RNDA
|
|
|| (spec.rnd_mode == MPFR_RNDD && MPFR_IS_NEG (p))
|
|
|| (spec.rnd_mode == MPFR_RNDU && MPFR_IS_POS (p))
|
|
|| (spec.rnd_mode == MPFR_RNDN && mpfr_cmp_d (y, 0.5) > 0))
|
|
/* rounded up to 1: one digit '1' in integral part.
|
|
note that 0.5 is rounded to 0 with RNDN (round ties to even) */
|
|
np->ip_ptr[0] = '1';
|
|
}
|
|
else
|
|
{
|
|
/* exp = position of the most significant decimal digit. */
|
|
round_to_10_power (&exp, p, 0, MPFR_RNDZ);
|
|
MPFR_ASSERTD (exp < 0);
|
|
|
|
if (exp < -spec.prec)
|
|
/* only the last digit may be non zero */
|
|
{
|
|
int round_away;
|
|
switch (spec.rnd_mode)
|
|
{
|
|
case MPFR_RNDA:
|
|
round_away = 1;
|
|
break;
|
|
case MPFR_RNDD:
|
|
round_away = MPFR_IS_NEG (p);
|
|
break;
|
|
case MPFR_RNDU:
|
|
round_away = MPFR_IS_POS (p);
|
|
break;
|
|
case MPFR_RNDN:
|
|
{
|
|
/* compare |p| to y = 0.5*10^(-spec.prec) */
|
|
mpfr_t y;
|
|
mpfr_exp_t e = MAX (MPFR_PREC (p), 56);
|
|
mpfr_init2 (y, e + 8);
|
|
do
|
|
{
|
|
/* find a lower approximation of
|
|
0.5*10^(-spec.prec) different from |p| */
|
|
e += 8;
|
|
mpfr_set_prec (y, e);
|
|
mpfr_set_si (y, -spec.prec, MPFR_RNDN);
|
|
mpfr_exp10 (y, y, MPFR_RNDD);
|
|
mpfr_div_2ui (y, y, 1, MPFR_RNDN);
|
|
} while (mpfr_cmpabs (y, p) == 0);
|
|
|
|
round_away = mpfr_cmpabs (y, p) < 0;
|
|
mpfr_clear (y);
|
|
}
|
|
break;
|
|
default:
|
|
round_away = 0;
|
|
}
|
|
|
|
if (round_away)
|
|
/* round away from zero: the last output digit is '1' */
|
|
{
|
|
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 zeros in fractional part */
|
|
{
|
|
MPFR_ASSERTD (!spec_g);
|
|
np->fp_leading_zeros = spec.prec;
|
|
}
|
|
}
|
|
else
|
|
/* the most significant digits are the last
|
|
spec.prec + exp + 1 digits in fractional part */
|
|
{
|
|
char *ptr;
|
|
size_t str_len;
|
|
size_t nsd = spec.prec + exp + 1;
|
|
/* WARNING: nsd may equal 1, but here we use 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);
|
|
if (MPFR_IS_NEG (p))
|
|
++str;
|
|
if (exp == 1)
|
|
/* round up to 1 */
|
|
{
|
|
MPFR_ASSERTD (str[0] == '1');
|
|
np->ip_ptr[0] = '1';
|
|
if (!spec_g || spec.alt)
|
|
np->fp_leading_zeros = spec.prec;
|
|
}
|
|
else
|
|
{
|
|
/* skip sign */
|
|
np->fp_ptr = str;
|
|
np->fp_leading_zeros = -exp;
|
|
MPFR_ASSERTD (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 many digits in fractional part */
|
|
return -1;
|
|
|
|
MPFR_ASSERTD (str_len > 0);
|
|
np->fp_size = str_len;
|
|
|
|
if ((!spec_g || spec.alt)
|
|
&& 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| */
|
|
{
|
|
size_t nsd; /* Number of significant digits */
|
|
|
|
/* Determine the position of the most significant decimal digit. */
|
|
round_to_10_power (&exp, p, 0, MPFR_RNDZ);
|
|
|
|
MPFR_ASSERTD (exp >= 0);
|
|
if (exp > INT_MAX)
|
|
/* P is too large to print all its integral part digits */
|
|
return -1;
|
|
|
|
np->ip_size = exp + 1;
|
|
|
|
nsd = 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 (exp > 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 = exp - 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 many digits in fractional part */
|
|
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
|
|
/* spec.prec digits in fractional part */
|
|
{
|
|
if (np->ip_size == exp - 1)
|
|
/* the absolute value of the number has been rounded up to a power
|
|
of ten.
|
|
Insert an additional zero in integral part and put the rest of
|
|
them in fractional part. */
|
|
np->ip_trailing_zeros = 1;
|
|
|
|
if (spec.prec != 0)
|
|
{
|
|
MPFR_ASSERTD (np->ip_size + np->ip_trailing_zeros == exp);
|
|
MPFR_ASSERTD (np->ip_size + spec.prec == nsd);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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.spec != 'G') || spec.alt))
|
|
/* fractional part */
|
|
{
|
|
np->point = MPFR_DECIMAL_POINT;
|
|
np->fp_trailing_zeros = (spec.spec == 'g' && spec.spec == '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 (spec.prec == -1)
|
|
spec.prec = 6;
|
|
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' and precision T-1. */
|
|
int threshold;
|
|
mpfr_exp_t x;
|
|
|
|
threshold = (spec.prec < 0) ? 6 : (spec.prec == 0) ? 1 : spec.prec;
|
|
round_to_10_power (&x, p, threshold - 1, spec.rnd_mode);
|
|
|
|
if (threshold > x && x >= -4)
|
|
{
|
|
/* the conversion is with style 'f' */
|
|
spec.prec = threshold - x - 1;
|
|
|
|
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 xgmp_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);
|
|
xgmp_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;
|
|
xgmp_fmt_flag = 1;
|
|
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 = MPFR_RNDD;
|
|
break;
|
|
case 'U':
|
|
++fmt;
|
|
spec.rnd_mode = MPFR_RNDU;
|
|
break;
|
|
case 'Y':
|
|
++fmt;
|
|
spec.rnd_mode = MPFR_RNDA;
|
|
break;
|
|
case 'Z':
|
|
++fmt;
|
|
spec.rnd_mode = MPFR_RNDZ;
|
|
break;
|
|
case 'N':
|
|
++fmt;
|
|
default:
|
|
spec.rnd_mode = MPFR_RNDN;
|
|
}
|
|
}
|
|
|
|
spec.spec = *fmt;
|
|
if (!specinfo_is_valid (spec))
|
|
goto error;
|
|
|
|
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 (xgmp_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 mpfr_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 (xgmp_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 (xgmp_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);
|
|
xgmp_fmt_flag = 1;
|
|
}
|
|
}
|
|
|
|
if (start != fmt)
|
|
FLUSH (xgmp_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);
|
|
buf.size = nbchar + 1; /* update needed for __gmp_free_func below when
|
|
nbchar is too large (overflow_error) */
|
|
*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 */
|