-/* Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+/* Copyright (C) 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
Contributed by Andy Vaught
Write float code factoring to this file by Jerry DeLisle
F2003 I/O support contributed by Jerry DeLisle
/* Output a real number according to its format which is FMT_G free. */
-static void
+static try
output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
int sign_bit, bool zero_flag, int ndigits, int edigits)
{
char *out;
char *digits;
- int e;
+ int e, w, d, p, i;
char expchar, rchar;
format_token ft;
- int w;
- int d;
/* Number of digits before the decimal point. */
int nbefore;
/* Number of zeros after the decimal point. */
int nzero_real;
int leadzero;
int nblanks;
- int i;
sign_t sign;
ft = f->format;
w = f->u.real.w;
d = f->u.real.d;
+ p = dtp->u.p.scale_factor;
rchar = '5';
nzero_real = -1;
/* Make sure zero comes out as 0.0e0. */
if (zero_flag)
- {
- e = 0;
- if (compile_options.sign_zero == 1)
- sign = calculate_sign (dtp, sign_bit);
- else
- sign = calculate_sign (dtp, 0);
-
- /* Handle special cases. */
- if (w == 0)
- w = d + 2;
-
- /* For this one we choose to not output a decimal point.
- F95 10.5.1.2.1 */
- if (w == 1 && ft == FMT_F)
- {
- out = write_block (dtp, w);
- if (out == NULL)
- return;
-
- if (unlikely (is_char4_unit (dtp)))
- {
- gfc_char4_t *out4 = (gfc_char4_t *) out;
- *out4 = '0';
- return;
- }
-
- *out = '0';
- return;
- }
-
- }
+ e = 0;
/* Normalize the fractional component. */
buffer[2] = buffer[1];
switch (ft)
{
case FMT_F:
- if (d == 0 && e <= 0 && dtp->u.p.scale_factor == 0)
+ if (d == 0 && e <= 0 && p == 0)
{
memmove (digits + 1, digits, ndigits - 1);
digits[0] = '0';
e++;
}
- nbefore = e + dtp->u.p.scale_factor;
+ nbefore = e + p;
if (nbefore < 0)
{
nzero = -nbefore;
case FMT_E:
case FMT_D:
i = dtp->u.p.scale_factor;
- if (d <= 0 && i == 0)
+ if (d <= 0 && p == 0)
{
generate_error (&dtp->common, LIBERROR_FORMAT, "Precision not "
"greater than zero in format specifier 'E' or 'D'");
- return;
+ return FAILURE;
}
- if (i <= -d || i >= d + 2)
+ if (p <= -d || p >= d + 2)
{
generate_error (&dtp->common, LIBERROR_FORMAT, "Scale factor "
"out of range in format specifier 'E' or 'D'");
- return;
+ return FAILURE;
}
if (!zero_flag)
- e -= i;
- if (i < 0)
+ e -= p;
+ if (p < 0)
{
nbefore = 0;
- nzero = -i;
- nafter = d + i;
+ nzero = -p;
+ nafter = d + p;
}
- else if (i > 0)
+ else if (p > 0)
{
- nbefore = i;
+ nbefore = p;
nzero = 0;
- nafter = (d - i) + 1;
+ nafter = (d - p) + 1;
}
- else /* i == 0 */
+ else /* p == 0 */
{
nbefore = 0;
nzero = 0;
internal_error (&dtp->common, "Unexpected format token");
}
+ if (zero_flag)
+ goto skip;
/* Round the value. The value being rounded is an unsigned magnitude.
The ROUND_COMPATIBLE is rounding away from zero when there is a tie. */
switch (dtp->u.p.current_unit->round_status)
if (sign_bit)
goto skip;
rchar = '0';
- break;
+ /* Scan for trailing zeros to see if we really need to round it. */
+ for(i = nbefore + nafter; i < ndigits; i++)
+ {
+ if (digits[i] != '0')
+ goto do_rnd;
+ }
+ goto skip;
case ROUND_DOWN:
if (!sign_bit)
goto skip;
}
else if (nbefore + nafter < ndigits)
{
- ndigits = nbefore + nafter;
- i = ndigits;
+ i = ndigits = nbefore + nafter;
if (digits[i] >= rchar)
{
/* Propagate the carry. */
else
edigits = 0;
- /* Zero values always output as positive, even if the value was negative
- before rounding. */
+ /* Scan the digits string and count the number of zeros. If we make it
+ all the way through the loop, we know the value is zero after the
+ rounding completed above. */
for (i = 0; i < ndigits; i++)
{
if (digits[i] != '0')
break;
}
+
+ /* To format properly, we need to know if the rounded result is zero and if
+ so, we set the zero_flag which may have been already set for
+ actual zero. */
if (i == ndigits)
{
+ zero_flag = true;
/* The output is zero, so set the sign according to the sign bit unless
-fno-sign-zero was specified. */
if (compile_options.sign_zero == 1)
sign = calculate_sign (dtp, 0);
}
- /* Pick a field size if none was specified. */
+ /* Pick a field size if none was specified, taking into account small
+ values that may have been rounded to zero. */
if (w <= 0)
- w = nbefore + nzero + nafter + (sign != S_NONE ? 2 : 1);
+ {
+ if (zero_flag)
+ w = d + (sign != S_NONE ? 2 : 1) + (d == 0 ? 1 : 0);
+ else
+ {
+ w = nbefore + nzero + nafter + (sign != S_NONE ? 2 : 1);
+ w = w == 1 ? 2 : w;
+ }
+ }
/* Work out how much padding is needed. */
nblanks = w - (nbefore + nzero + nafter + edigits + 1);
/* Create the ouput buffer. */
out = write_block (dtp, w);
if (out == NULL)
- return;
+ return FAILURE;
/* Check the value fits in the specified field width. */
- if (nblanks < 0 || edigits == -1)
+ if (nblanks < 0 || edigits == -1 || w == 1 || (w == 2 && sign != S_NONE))
{
if (unlikely (is_char4_unit (dtp)))
{
gfc_char4_t *out4 = (gfc_char4_t *) out;
memset4 (out4, '*', w);
- return;
+ return FAILURE;
}
star_fill (out, w);
- return;
+ return FAILURE;
}
/* See if we have space for a zero before the decimal point. */
*(out4++) = expchar;
edigits--;
}
-#if HAVE_SNPRINTF
snprintf (buffer, size, "%+0*d", edigits, e);
-#else
- sprintf (buffer, "%+0*d", edigits, e);
-#endif
memcpy4 (out4, buffer, edigits);
}
memset4 (out4, ' ' , nblanks);
dtp->u.p.no_leading_blank = 0;
}
- return;
+ return SUCCESS;
} /* End of character(kind=4) internal unit code. */
/* Pad to full field width. */
*(out++) = expchar;
edigits--;
}
-#if HAVE_SNPRINTF
snprintf (buffer, size, "%+0*d", edigits, e);
-#else
- sprintf (buffer, "%+0*d", edigits, e);
-#endif
memcpy (out, buffer, edigits);
}
#undef STR
#undef STR1
#undef MIN_FIELD_WIDTH
+ return SUCCESS;
}
{
char * p, fin;
int nb = 0;
+ sign_t sign;
+ int mark;
if (f->format != FMT_B && f->format != FMT_O && f->format != FMT_Z)
{
+ sign = calculate_sign (dtp, sign_bit);
+ mark = (sign == S_PLUS || sign == S_MINUS) ? 8 : 7;
+
nb = f->u.real.w;
-
+
/* If the field width is zero, the processor must select a width
not zero. 4 is chosen to allow output of '-Inf' or '+Inf' */
- if (nb == 0) nb = 4;
+ if ((nb == 0) || dtp->u.p.g0_no_blanks)
+ {
+ if (isnan_flag)
+ nb = 3;
+ else
+ nb = (sign == S_PLUS || sign == S_MINUS) ? 4 : 3;
+ }
p = write_block (dtp, nb);
if (p == NULL)
return;
if (unlikely (is_char4_unit (dtp)))
{
gfc_char4_t *p4 = (gfc_char4_t *) p;
- if (nb > 8)
+
+ if (nb > mark)
/* We have room, so output 'Infinity' */
memcpy4 (p4 + nb - 8, "Infinity", 8);
else
- /* For the case of width equals 8, there is not enough room
+ /* For the case of width equals mark, there is not enough room
for the sign and 'Infinity' so we go with 'Inf' */
memcpy4 (p4 + nb - 3, "Inf", 3);
- if (nb < 9 && nb > 3)
- /* Put the sign in front of Inf */
- p4[nb - 4] = (gfc_char4_t) fin;
- else if (nb > 8)
- /* Put the sign in front of Infinity */
- p4[nb - 9] = (gfc_char4_t) fin;
+ if (sign == S_PLUS || sign == S_MINUS)
+ {
+ if (nb < 9 && nb > 3)
+ /* Put the sign in front of Inf */
+ p4[nb - 4] = (gfc_char4_t) fin;
+ else if (nb > 8)
+ /* Put the sign in front of Infinity */
+ p4[nb - 9] = (gfc_char4_t) fin;
+ }
return;
}
- if (nb > 8)
+ if (nb > mark)
/* We have room, so output 'Infinity' */
memcpy(p + nb - 8, "Infinity", 8);
else
for the sign and 'Infinity' so we go with 'Inf' */
memcpy(p + nb - 3, "Inf", 3);
- if (nb < 9 && nb > 3)
- p[nb - 4] = fin; /* Put the sign in front of Inf */
- else if (nb > 8)
- p[nb - 9] = fin; /* Put the sign in front of Infinity */
+ if (sign == S_PLUS || sign == S_MINUS)
+ {
+ if (nb < 9 && nb > 3)
+ p[nb - 4] = fin; /* Put the sign in front of Inf */
+ else if (nb > 8)
+ p[nb - 9] = fin; /* Put the sign in front of Infinity */
+ }
}
else
{
m >= 10**d-0.5 Ew.d[Ee]
notes: for Gw.d , n' ' means 4 blanks
- for Gw.dEe, n' ' means e+2 blanks */
+ for Gw.dEe, n' ' means e+2 blanks
+ for rounding modes adjustment, r, See Fortran F2008 10.7.5.2.2
+ the asm volatile is required for 32-bit x86 platforms. */
#define OUTPUT_FLOAT_FMT_G(x) \
static void \
output_float_FMT_G_ ## x (st_parameter_dt *dtp, const fnode *f, \
GFC_REAL_ ## x m, char *buffer, size_t size, \
- int sign_bit, bool zero_flag, int ndigits, int edigits) \
+ int sign_bit, bool zero_flag, int ndigits, \
+ int edigits, int comp_d) \
{ \
int e = f->u.real.e;\
int d = f->u.real.d;\
int w = f->u.real.w;\
fnode *newf;\
- GFC_REAL_ ## x rexp_d;\
+ GFC_REAL_ ## x rexp_d, r = 0.5;\
int low, high, mid;\
int ubound, lbound;\
- char *p;\
+ char *p, pad = ' ';\
int save_scale_factor, nb = 0;\
+ try result;\
\
save_scale_factor = dtp->u.p.scale_factor;\
newf = (fnode *) get_mem (sizeof (fnode));\
\
+ switch (dtp->u.p.current_unit->round_status)\
+ {\
+ case ROUND_ZERO:\
+ r = sign_bit ? 1.0 : 0.0;\
+ break;\
+ case ROUND_UP:\
+ r = 1.0;\
+ break;\
+ case ROUND_DOWN:\
+ r = 0.0;\
+ break;\
+ default:\
+ break;\
+ }\
+\
rexp_d = calculate_exp_ ## x (-d);\
- if ((m > 0.0 && m < 0.1 - 0.05 * rexp_d) || (rexp_d * (m + 0.5) >= 1.0) ||\
- ((m == 0.0) && !(compile_options.allow_std & GFC_STD_F2003)))\
+ if ((m > 0.0 && ((m < 0.1 - 0.1 * r * rexp_d) || (rexp_d * (m + r) >= 1.0)))\
+ || ((m == 0.0) && !(compile_options.allow_std\
+ & (GFC_STD_F2003 | GFC_STD_F2008))))\
{ \
newf->format = FMT_E;\
newf->u.real.w = w;\
- newf->u.real.d = d;\
+ newf->u.real.d = d - comp_d;\
newf->u.real.e = e;\
nb = 0;\
goto finish;\
\
while (low <= high)\
{ \
- GFC_REAL_ ## x temp;\
+ volatile GFC_REAL_ ## x temp;\
mid = (low + high) / 2;\
\
- temp = (calculate_exp_ ## x (mid - 1) * (1 - 0.5 * rexp_d));\
+ temp = (calculate_exp_ ## x (mid - 1) * (1 - r * rexp_d));\
\
if (m < temp)\
{ \
}\
}\
\
- if (e < 0)\
- nb = 4;\
- else\
- nb = e + 2;\
-\
+ nb = e <= 0 ? 4 : e + 2;\
+ nb = nb >= w ? w - 1 : nb;\
newf->format = FMT_F;\
- newf->u.real.w = f->u.real.w - nb;\
-\
- if (m == 0.0)\
- newf->u.real.d = d - 1;\
- else\
- newf->u.real.d = - (mid - d - 1);\
-\
+ newf->u.real.w = w - nb;\
+ newf->u.real.d = m == 0.0 ? d - 1 : -(mid - d - 1) ;\
dtp->u.p.scale_factor = 0;\
\
finish:\
- output_float (dtp, newf, buffer, size, sign_bit, zero_flag, ndigits, \
- edigits);\
+ result = output_float (dtp, newf, buffer, size, sign_bit, zero_flag, \
+ ndigits, edigits);\
dtp->u.p.scale_factor = save_scale_factor;\
\
free (newf);\
p = write_block (dtp, nb);\
if (p == NULL)\
return;\
+ if (result == FAILURE)\
+ pad = '*';\
if (unlikely (is_char4_unit (dtp)))\
{\
gfc_char4_t *p4 = (gfc_char4_t *) p;\
- memset4 (p4, ' ', nb);\
+ memset4 (p4, pad, nb);\
}\
- else\
- memset (p, ' ', nb);\
+ else \
+ memset (p, pad, nb);\
}\
}\
/* Define a macro to build code for write_float. */
- /* Note: Before output_float is called, sprintf is used to print to buffer the
+ /* Note: Before output_float is called, snprintf is used to print to buffer the
number in the format +D.DDDDe+ddd. For an N digit exponent, this gives us
(MIN_FIELD_WIDTH-5)-N digits after the decimal point, plus another one
before the decimal point.
equal to the precision. The exponent always contains at least two
digits; if the value is zero, the exponent is 00. */
-#ifdef HAVE_SNPRINTF
-
#define DTOA \
snprintf (buffer, size, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
"e", ndigits - 1, tmp);
snprintf (buffer, size, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
"Le", ndigits - 1, tmp);
-#else
-
-#define DTOA \
-sprintf (buffer, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
- "e", ndigits - 1, tmp);
-
-#define DTOAL \
-sprintf (buffer, "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
- "Le", ndigits - 1, tmp);
-
-#endif
#if defined(GFC_REAL_16_IS_FLOAT128)
#define DTOAQ \
-__qmath_(quadmath_dtoaq) (buffer, size, ndigits - 1, tmp);
+__qmath_(quadmath_snprintf) (buffer, sizeof buffer, \
+ "%+-#" STR(MIN_FIELD_WIDTH) ".*" \
+ "Qe", ndigits - 1, tmp);
#endif
#define WRITE_FLOAT(x,y)\
edigits);\
else \
output_float_FMT_G_ ## x (dtp, f, tmp, buffer, size, sign_bit, \
- zero_flag, ndigits, edigits);\
+ zero_flag, ndigits, edigits, comp_d);\
}\
/* Output a real number according to its format. */
static void
-write_float (st_parameter_dt *dtp, const fnode *f, const char *source, int len)
+write_float (st_parameter_dt *dtp, const fnode *f, const char *source, \
+ int len, int comp_d)
{
#if defined(HAVE_GFC_REAL_16) || __LDBL_DIG__ > 18
-# define MIN_FIELD_WIDTH 46
+# define MIN_FIELD_WIDTH 49
#else
-# define MIN_FIELD_WIDTH 31
+# define MIN_FIELD_WIDTH 32
#endif
#define STR(x) STR1(x)
#define STR1(x) #x
to handle the largest number of exponent digits expected. */
edigits=4;
- if (f->format == FMT_F || f->format == FMT_EN || f->format == FMT_G
- || ((f->format == FMT_D || f->format == FMT_E)
- && dtp->u.p.scale_factor != 0))
- {
- /* Always convert at full precision to avoid double rounding. */
- ndigits = MIN_FIELD_WIDTH - 4 - edigits;
- }
- else
- {
- /* The number of digits is known, so let printf do the rounding. */
- if (f->format == FMT_ES)
- ndigits = f->u.real.d + 1;
- else
- ndigits = f->u.real.d;
- if (ndigits > MIN_FIELD_WIDTH - 4 - edigits)
- ndigits = MIN_FIELD_WIDTH - 4 - edigits;
- }
+ /* Always convert at full precision to avoid double rounding. */
+ ndigits = MIN_FIELD_WIDTH - 4 - edigits;
switch (len)
{