1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318 |
- /* Copyright (C) 2007-2015 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
- This file is part of the GNU Fortran runtime library (libgfortran).
- Libgfortran is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3, or (at your option)
- any later version.
- Libgfortran is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- Under Section 7 of GPL version 3, you are granted additional
- permissions described in the GCC Runtime Library Exception, version
- 3.1, as published by the Free Software Foundation.
- You should have received a copy of the GNU General Public License and
- a copy of the GCC Runtime Library Exception along with this program;
- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
- <http://www.gnu.org/licenses/>. */
- #include "config.h"
- typedef enum
- { S_NONE, S_MINUS, S_PLUS }
- sign_t;
- /* Given a flag that indicates if a value is negative or not, return a
- sign_t that gives the sign that we need to produce. */
- static sign_t
- calculate_sign (st_parameter_dt *dtp, int negative_flag)
- {
- sign_t s = S_NONE;
- if (negative_flag)
- s = S_MINUS;
- else
- switch (dtp->u.p.sign_status)
- {
- case SIGN_SP: /* Show sign. */
- s = S_PLUS;
- break;
- case SIGN_SS: /* Suppress sign. */
- s = S_NONE;
- break;
- case SIGN_S: /* Processor defined. */
- case SIGN_UNSPECIFIED:
- s = options.optional_plus ? S_PLUS : S_NONE;
- break;
- }
- return s;
- }
- /* Determine the precision except for EN format. For G format,
- determines an upper bound to be used for sizing the buffer. */
- static int
- determine_precision (st_parameter_dt * dtp, const fnode * f, int len)
- {
- int precision = f->u.real.d;
- switch (f->format)
- {
- case FMT_F:
- case FMT_G:
- precision += dtp->u.p.scale_factor;
- break;
- case FMT_ES:
- /* Scale factor has no effect on output. */
- break;
- case FMT_E:
- case FMT_D:
- /* See F2008 10.7.2.3.3.6 */
- if (dtp->u.p.scale_factor <= 0)
- precision += dtp->u.p.scale_factor - 1;
- break;
- default:
- return -1;
- }
- /* If the scale factor has a large negative value, we must do our
- own rounding? Use ROUND='NEAREST', which should be what snprintf
- is using as well. */
- if (precision < 0 &&
- (dtp->u.p.current_unit->round_status == ROUND_UNSPECIFIED
- || dtp->u.p.current_unit->round_status == ROUND_PROCDEFINED))
- dtp->u.p.current_unit->round_status = ROUND_NEAREST;
- /* Add extra guard digits up to at least full precision when we do
- our own rounding. */
- if (dtp->u.p.current_unit->round_status != ROUND_UNSPECIFIED
- && dtp->u.p.current_unit->round_status != ROUND_PROCDEFINED)
- {
- precision += 2 * len + 4;
- if (precision < 0)
- precision = 0;
- }
- return precision;
- }
- /* Output a real number according to its format which is FMT_G free. */
- static bool
- output_float (st_parameter_dt *dtp, const fnode *f, char *buffer, size_t size,
- int nprinted, int precision, int sign_bit, bool zero_flag)
- {
- char *out;
- char *digits;
- int e, w, d, p, i;
- char expchar, rchar;
- format_token ft;
- /* Number of digits before the decimal point. */
- int nbefore;
- /* Number of zeros after the decimal point. */
- int nzero;
- /* Number of digits after the decimal point. */
- int nafter;
- int leadzero;
- int nblanks;
- int ndigits, edigits;
- sign_t sign;
- ft = f->format;
- w = f->u.real.w;
- d = f->u.real.d;
- p = dtp->u.p.scale_factor;
- rchar = '5';
- /* We should always know the field width and precision. */
- if (d < 0)
- internal_error (&dtp->common, "Unspecified precision");
- sign = calculate_sign (dtp, sign_bit);
-
- /* Calculate total number of digits. */
- if (ft == FMT_F)
- ndigits = nprinted - 2;
- else
- ndigits = precision + 1;
- /* Read the exponent back in. */
- if (ft != FMT_F)
- e = atoi (&buffer[ndigits + 3]) + 1;
- else
- e = 0;
- /* Make sure zero comes out as 0.0e0. */
- if (zero_flag)
- e = 0;
- /* Normalize the fractional component. */
- if (ft != FMT_F)
- {
- buffer[2] = buffer[1];
- digits = &buffer[2];
- }
- else
- digits = &buffer[1];
- /* Figure out where to place the decimal point. */
- switch (ft)
- {
- case FMT_F:
- nbefore = ndigits - precision;
- /* Make sure the decimal point is a '.'; depending on the
- locale, this might not be the case otherwise. */
- digits[nbefore] = '.';
- if (p != 0)
- {
- if (p > 0)
- {
-
- memmove (digits + nbefore, digits + nbefore + 1, p);
- digits[nbefore + p] = '.';
- nbefore += p;
- nafter = d - p;
- if (nafter < 0)
- nafter = 0;
- nafter = d;
- nzero = 0;
- }
- else /* p < 0 */
- {
- if (nbefore + p >= 0)
- {
- nzero = 0;
- memmove (digits + nbefore + p + 1, digits + nbefore + p, -p);
- nbefore += p;
- digits[nbefore] = '.';
- nafter = d;
- }
- else
- {
- nzero = -(nbefore + p);
- memmove (digits + 1, digits, nbefore);
- digits++;
- nafter = d + nbefore;
- nbefore = 0;
- }
- if (nzero > d)
- nzero = d;
- }
- }
- else
- {
- nzero = 0;
- nafter = d;
- }
- while (digits[0] == '0' && nbefore > 0)
- {
- digits++;
- nbefore--;
- ndigits--;
- }
- expchar = 0;
- /* If we need to do rounding ourselves, get rid of the dot by
- moving the fractional part. */
- if (dtp->u.p.current_unit->round_status != ROUND_UNSPECIFIED
- && dtp->u.p.current_unit->round_status != ROUND_PROCDEFINED)
- memmove (digits + nbefore, digits + nbefore + 1, ndigits - nbefore);
- break;
- case FMT_E:
- case FMT_D:
- i = dtp->u.p.scale_factor;
- if (d <= 0 && p == 0)
- {
- generate_error (&dtp->common, LIBERROR_FORMAT, "Precision not "
- "greater than zero in format specifier 'E' or 'D'");
- return false;
- }
- if (p <= -d || p >= d + 2)
- {
- generate_error (&dtp->common, LIBERROR_FORMAT, "Scale factor "
- "out of range in format specifier 'E' or 'D'");
- return false;
- }
- if (!zero_flag)
- e -= p;
- if (p < 0)
- {
- nbefore = 0;
- nzero = -p;
- nafter = d + p;
- }
- else if (p > 0)
- {
- nbefore = p;
- nzero = 0;
- nafter = (d - p) + 1;
- }
- else /* p == 0 */
- {
- nbefore = 0;
- nzero = 0;
- nafter = d;
- }
- if (ft == FMT_E)
- expchar = 'E';
- else
- expchar = 'D';
- break;
- case FMT_EN:
- /* The exponent must be a multiple of three, with 1-3 digits before
- the decimal point. */
- if (!zero_flag)
- e--;
- if (e >= 0)
- nbefore = e % 3;
- else
- {
- nbefore = (-e) % 3;
- if (nbefore != 0)
- nbefore = 3 - nbefore;
- }
- e -= nbefore;
- nbefore++;
- nzero = 0;
- nafter = d;
- expchar = 'E';
- break;
- case FMT_ES:
- if (!zero_flag)
- e--;
- nbefore = 1;
- nzero = 0;
- nafter = d;
- expchar = 'E';
- break;
- default:
- /* Should never happen. */
- internal_error (&dtp->common, "Unexpected format token");
- }
- if (zero_flag)
- goto skip;
- /* Round the value. The value being rounded is an unsigned magnitude. */
- switch (dtp->u.p.current_unit->round_status)
- {
- /* For processor defined and unspecified rounding we use
- snprintf to print the exact number of digits needed, and thus
- let snprintf handle the rounding. On system claiming support
- for IEEE 754, this ought to be round to nearest, ties to
- even, corresponding to the Fortran ROUND='NEAREST'. */
- case ROUND_PROCDEFINED:
- case ROUND_UNSPECIFIED:
- case ROUND_ZERO: /* Do nothing and truncation occurs. */
- goto skip;
- case ROUND_UP:
- if (sign_bit)
- goto skip;
- goto updown;
- case ROUND_DOWN:
- if (!sign_bit)
- goto skip;
- goto updown;
- case ROUND_NEAREST:
- /* Round compatible unless there is a tie. A tie is a 5 with
- all trailing zero's. */
- i = nafter + nbefore;
- if (digits[i] == '5')
- {
- for(i++ ; i < ndigits; i++)
- {
- if (digits[i] != '0')
- goto do_rnd;
- }
- /* It is a tie so round to even. */
- switch (digits[nafter + nbefore - 1])
- {
- case '1':
- case '3':
- case '5':
- case '7':
- case '9':
- /* If odd, round away from zero to even. */
- break;
- default:
- /* If even, skip rounding, truncate to even. */
- goto skip;
- }
- }
- /* Fall through. */
- /* The ROUND_COMPATIBLE is rounding away from zero when there is a tie. */
- case ROUND_COMPATIBLE:
- rchar = '5';
- goto do_rnd;
- }
- updown:
- rchar = '0';
- if (ft != FMT_F && w > 0 && d == 0 && p == 0)
- nbefore = 1;
- /* 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;
-
- do_rnd:
-
- if (nbefore + nafter == 0)
- /* Handle the case Fw.0 and value < 1.0 */
- {
- ndigits = 0;
- if (digits[0] >= rchar)
- {
- /* We rounded to zero but shouldn't have */
- nbefore = 1;
- digits--;
- digits[0] = '1';
- ndigits = 1;
- }
- }
- else if (nbefore + nafter < ndigits)
- {
- i = ndigits = nbefore + nafter;
- if (digits[i] >= rchar)
- {
- /* Propagate the carry. */
- for (i--; i >= 0; i--)
- {
- if (digits[i] != '9')
- {
- digits[i]++;
- break;
- }
- digits[i] = '0';
- }
- if (i < 0)
- {
- /* The carry overflowed. Fortunately we have some spare
- space at the start of the buffer. We may discard some
- digits, but this is ok because we already know they are
- zero. */
- digits--;
- digits[0] = '1';
- if (ft == FMT_F)
- {
- if (nzero > 0)
- {
- nzero--;
- nafter++;
- }
- else
- nbefore++;
- }
- else if (ft == FMT_EN)
- {
- nbefore++;
- if (nbefore == 4)
- {
- nbefore = 1;
- e += 3;
- }
- }
- else
- e++;
- }
- }
- }
- skip:
- /* Calculate the format of the exponent field. */
- if (expchar && !(dtp->u.p.g0_no_blanks && e == 0))
- {
- edigits = 1;
- for (i = abs (e); i >= 10; i /= 10)
- edigits++;
- if (f->u.real.e < 0)
- {
- /* Width not specified. Must be no more than 3 digits. */
- if (e > 999 || e < -999)
- edigits = -1;
- else
- {
- edigits = 4;
- if (e > 99 || e < -99)
- expchar = ' ';
- }
- }
- else
- {
- /* Exponent width specified, check it is wide enough. */
- if (edigits > f->u.real.e)
- edigits = -1;
- else
- edigits = f->u.real.e + 2;
- }
- }
- else
- edigits = 0;
- /* 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. */
- int hasdot = 0;
- for (i = 0; i < ndigits + hasdot; i++)
- {
- if (digits[i] == '.')
- hasdot = 1;
- else 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 + hasdot)
- {
- 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, sign_bit);
- else
- sign = calculate_sign (dtp, 0);
- }
- /* Pick a field size if none was specified, taking into account small
- values that may have been rounded to zero. */
- if (w <= 0)
- {
- 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);
- if (sign != S_NONE)
- nblanks--;
- if (dtp->u.p.g0_no_blanks)
- {
- w -= nblanks;
- nblanks = 0;
- }
- /* Create the ouput buffer. */
- out = write_block (dtp, w);
- if (out == NULL)
- return false;
- /* Check the value fits in the specified field width. */
- 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 false;
- }
- star_fill (out, w);
- return false;
- }
- /* See if we have space for a zero before the decimal point. */
- if (nbefore == 0 && nblanks > 0)
- {
- leadzero = 1;
- nblanks--;
- }
- else
- leadzero = 0;
- /* For internal character(kind=4) units, we duplicate the code used for
- regular output slightly modified. This needs to be maintained
- consistent with the regular code that follows this block. */
- if (unlikely (is_char4_unit (dtp)))
- {
- gfc_char4_t *out4 = (gfc_char4_t *) out;
- /* Pad to full field width. */
- if ( ( nblanks > 0 ) && !dtp->u.p.no_leading_blank)
- {
- memset4 (out4, ' ', nblanks);
- out4 += nblanks;
- }
- /* Output the initial sign (if any). */
- if (sign == S_PLUS)
- *(out4++) = '+';
- else if (sign == S_MINUS)
- *(out4++) = '-';
- /* Output an optional leading zero. */
- if (leadzero)
- *(out4++) = '0';
- /* Output the part before the decimal point, padding with zeros. */
- if (nbefore > 0)
- {
- if (nbefore > ndigits)
- {
- i = ndigits;
- memcpy4 (out4, digits, i);
- ndigits = 0;
- while (i < nbefore)
- out4[i++] = '0';
- }
- else
- {
- i = nbefore;
- memcpy4 (out4, digits, i);
- ndigits -= i;
- }
- digits += i;
- out4 += nbefore;
- }
- /* Output the decimal point. */
- *(out4++) = dtp->u.p.current_unit->decimal_status
- == DECIMAL_POINT ? '.' : ',';
- if (ft == FMT_F
- && (dtp->u.p.current_unit->round_status == ROUND_UNSPECIFIED
- || dtp->u.p.current_unit->round_status == ROUND_PROCDEFINED))
- digits++;
- /* Output leading zeros after the decimal point. */
- if (nzero > 0)
- {
- for (i = 0; i < nzero; i++)
- *(out4++) = '0';
- }
- /* Output digits after the decimal point, padding with zeros. */
- if (nafter > 0)
- {
- if (nafter > ndigits)
- i = ndigits;
- else
- i = nafter;
- memcpy4 (out4, digits, i);
- while (i < nafter)
- out4[i++] = '0';
- digits += i;
- ndigits -= i;
- out4 += nafter;
- }
- /* Output the exponent. */
- if (expchar && !(dtp->u.p.g0_no_blanks && e == 0))
- {
- if (expchar != ' ')
- {
- *(out4++) = expchar;
- edigits--;
- }
- snprintf (buffer, size, "%+0*d", edigits, e);
- memcpy4 (out4, buffer, edigits);
- }
- if (dtp->u.p.no_leading_blank)
- {
- out4 += edigits;
- memset4 (out4, ' ' , nblanks);
- dtp->u.p.no_leading_blank = 0;
- }
- return true;
- } /* End of character(kind=4) internal unit code. */
- /* Pad to full field width. */
- if ( ( nblanks > 0 ) && !dtp->u.p.no_leading_blank)
- {
- memset (out, ' ', nblanks);
- out += nblanks;
- }
- /* Output the initial sign (if any). */
- if (sign == S_PLUS)
- *(out++) = '+';
- else if (sign == S_MINUS)
- *(out++) = '-';
- /* Output an optional leading zero. */
- if (leadzero)
- *(out++) = '0';
- /* Output the part before the decimal point, padding with zeros. */
- if (nbefore > 0)
- {
- if (nbefore > ndigits)
- {
- i = ndigits;
- memcpy (out, digits, i);
- ndigits = 0;
- while (i < nbefore)
- out[i++] = '0';
- }
- else
- {
- i = nbefore;
- memcpy (out, digits, i);
- ndigits -= i;
- }
- digits += i;
- out += nbefore;
- }
- /* Output the decimal point. */
- *(out++) = dtp->u.p.current_unit->decimal_status == DECIMAL_POINT ? '.' : ',';
- if (ft == FMT_F
- && (dtp->u.p.current_unit->round_status == ROUND_UNSPECIFIED
- || dtp->u.p.current_unit->round_status == ROUND_PROCDEFINED))
- digits++;
- /* Output leading zeros after the decimal point. */
- if (nzero > 0)
- {
- for (i = 0; i < nzero; i++)
- *(out++) = '0';
- }
- /* Output digits after the decimal point, padding with zeros. */
- if (nafter > 0)
- {
- if (nafter > ndigits)
- i = ndigits;
- else
- i = nafter;
- memcpy (out, digits, i);
- while (i < nafter)
- out[i++] = '0';
- digits += i;
- ndigits -= i;
- out += nafter;
- }
- /* Output the exponent. */
- if (expchar && !(dtp->u.p.g0_no_blanks && e == 0))
- {
- if (expchar != ' ')
- {
- *(out++) = expchar;
- edigits--;
- }
- snprintf (buffer, size, "%+0*d", edigits, e);
- memcpy (out, buffer, edigits);
- }
- if (dtp->u.p.no_leading_blank)
- {
- out += edigits;
- memset( out , ' ' , nblanks );
- dtp->u.p.no_leading_blank = 0;
- }
- return true;
- }
- /* Write "Infinite" or "Nan" as appropriate for the given format. */
- static void
- write_infnan (st_parameter_dt *dtp, const fnode *f, int isnan_flag, int sign_bit)
- {
- 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) || 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 (nb < 3)
- {
- if (unlikely (is_char4_unit (dtp)))
- {
- gfc_char4_t *p4 = (gfc_char4_t *) p;
- memset4 (p4, '*', nb);
- }
- else
- memset (p, '*', nb);
- return;
- }
- if (unlikely (is_char4_unit (dtp)))
- {
- gfc_char4_t *p4 = (gfc_char4_t *) p;
- memset4 (p4, ' ', nb);
- }
- else
- memset(p, ' ', nb);
- if (!isnan_flag)
- {
- if (sign_bit)
- {
- /* If the sign is negative and the width is 3, there is
- insufficient room to output '-Inf', so output asterisks */
- if (nb == 3)
- {
- if (unlikely (is_char4_unit (dtp)))
- {
- gfc_char4_t *p4 = (gfc_char4_t *) p;
- memset4 (p4, '*', nb);
- }
- else
- memset (p, '*', nb);
- return;
- }
- /* The negative sign is mandatory */
- fin = '-';
- }
- else
- /* The positive sign is optional, but we output it for
- consistency */
- fin = '+';
-
- if (unlikely (is_char4_unit (dtp)))
- {
- gfc_char4_t *p4 = (gfc_char4_t *) p;
- if (nb > mark)
- /* We have room, so output 'Infinity' */
- memcpy4 (p4 + nb - 8, "Infinity", 8);
- else
- /* 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 (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 > mark)
- /* We have room, so output 'Infinity' */
- memcpy(p + nb - 8, "Infinity", 8);
- else
- /* For the case of width equals 8, there is not enough room
- for the sign and 'Infinity' so we go with 'Inf' */
- memcpy(p + nb - 3, "Inf", 3);
- 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
- {
- if (unlikely (is_char4_unit (dtp)))
- {
- gfc_char4_t *p4 = (gfc_char4_t *) p;
- memcpy4 (p4 + nb - 3, "NaN", 3);
- }
- else
- memcpy(p + nb - 3, "NaN", 3);
- }
- return;
- }
- }
- /* Returns the value of 10**d. */
- #define CALCULATE_EXP(x) \
- static GFC_REAL_ ## x \
- calculate_exp_ ## x (int d)\
- {\
- int i;\
- GFC_REAL_ ## x r = 1.0;\
- for (i = 0; i< (d >= 0 ? d : -d); i++)\
- r *= 10;\
- r = (d >= 0) ? r : 1.0 / r;\
- return r;\
- }
- CALCULATE_EXP(4)
- CALCULATE_EXP(8)
- #ifdef HAVE_GFC_REAL_10
- CALCULATE_EXP(10)
- #endif
- #ifdef HAVE_GFC_REAL_16
- CALCULATE_EXP(16)
- #endif
- #undef CALCULATE_EXP
- /* Define a macro to build code for write_float. */
- /* Note: Before output_float is called, snprintf is used to print to buffer the
- number in the format +D.DDDDe+ddd.
- # The result will always contain a decimal point, even if no
- digits follow it
- - The converted value is to be left adjusted on the field boundary
- + A sign (+ or -) always be placed before a number
- * prec is used as the precision
- e format: [-]d.ddde±dd where there is one digit before the
- decimal-point character and the number of digits after it is
- equal to the precision. The exponent always contains at least two
- digits; if the value is zero, the exponent is 00. */
- #define TOKENPASTE(x, y) TOKENPASTE2(x, y)
- #define TOKENPASTE2(x, y) x ## y
- #define DTOA(suff,prec,val) TOKENPASTE(DTOA2,suff)(prec,val)
- #define DTOA2(prec,val) \
- snprintf (buffer, size, "%+-#.*e", (prec), (val))
- #define DTOA2L(prec,val) \
- snprintf (buffer, size, "%+-#.*Le", (prec), (val))
- #if defined(GFC_REAL_16_IS_FLOAT128)
- #define DTOA2Q(prec,val) \
- __qmath_(quadmath_snprintf) (buffer, size, "%+-#.*Qe", (prec), (val))
- #endif
- #define FDTOA(suff,prec,val) TOKENPASTE(FDTOA2,suff)(prec,val)
- /* For F format, we print to the buffer with f format. */
- #define FDTOA2(prec,val) \
- snprintf (buffer, size, "%+-#.*f", (prec), (val))
- #define FDTOA2L(prec,val) \
- snprintf (buffer, size, "%+-#.*Lf", (prec), (val))
- #if defined(GFC_REAL_16_IS_FLOAT128)
- #define FDTOA2Q(prec,val) \
- __qmath_(quadmath_snprintf) (buffer, size, "%+-#.*Qf", \
- (prec), (val))
- #endif
- #if defined(GFC_REAL_16_IS_FLOAT128)
- #define ISFINITE2Q(val) finiteq(val)
- #endif
- #define ISFINITE2(val) isfinite(val)
- #define ISFINITE2L(val) isfinite(val)
- #define ISFINITE(suff,val) TOKENPASTE(ISFINITE2,suff)(val)
- #if defined(GFC_REAL_16_IS_FLOAT128)
- #define SIGNBIT2Q(val) signbitq(val)
- #endif
- #define SIGNBIT2(val) signbit(val)
- #define SIGNBIT2L(val) signbit(val)
- #define SIGNBIT(suff,val) TOKENPASTE(SIGNBIT2,suff)(val)
- #if defined(GFC_REAL_16_IS_FLOAT128)
- #define ISNAN2Q(val) isnanq(val)
- #endif
- #define ISNAN2(val) isnan(val)
- #define ISNAN2L(val) isnan(val)
- #define ISNAN(suff,val) TOKENPASTE(ISNAN2,suff)(val)
- /* Generate corresponding I/O format for FMT_G and output.
- The rules to translate FMT_G to FMT_E or FMT_F from DEC fortran
- LRM (table 11-2, Chapter 11, "I/O Formatting", P11-25) is:
- Data Magnitude Equivalent Conversion
- 0< m < 0.1-0.5*10**(-d-1) Ew.d[Ee]
- m = 0 F(w-n).(d-1), n' '
- 0.1-0.5*10**(-d-1)<= m < 1-0.5*10**(-d) F(w-n).d, n' '
- 1-0.5*10**(-d)<= m < 10-0.5*10**(-d+1) F(w-n).(d-1), n' '
- 10-0.5*10**(-d+1)<= m < 100-0.5*10**(-d+2) F(w-n).(d-2), n' '
- ................ ..........
- 10**(d-1)-0.5*10**(-1)<= m <10**d-0.5 F(w-n).0,n(' ')
- 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 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,y) \
- 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 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 exp_d, r = 0.5, r_sc;\
- int low, high, mid;\
- int ubound, lbound;\
- char *p, pad = ' ';\
- int save_scale_factor, nb = 0;\
- bool result;\
- int nprinted, precision;\
- volatile GFC_REAL_ ## x temp;\
- \
- save_scale_factor = dtp->u.p.scale_factor;\
- \
- 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;\
- }\
- \
- exp_d = calculate_exp_ ## x (d);\
- r_sc = (1 - r / exp_d);\
- temp = 0.1 * r_sc;\
- if ((m > 0.0 && ((m < temp) || (r >= (exp_d - m))))\
- || ((m == 0.0) && !(compile_options.allow_std\
- & (GFC_STD_F2003 | GFC_STD_F2008)))\
- || d == 0)\
- { \
- newf.format = FMT_E;\
- newf.u.real.w = w;\
- newf.u.real.d = d - comp_d;\
- newf.u.real.e = e;\
- nb = 0;\
- precision = determine_precision (dtp, &newf, x);\
- nprinted = DTOA(y,precision,m); \
- goto finish;\
- }\
- \
- mid = 0;\
- low = 0;\
- high = d + 1;\
- lbound = 0;\
- ubound = d + 1;\
- \
- while (low <= high)\
- { \
- mid = (low + high) / 2;\
- \
- temp = (calculate_exp_ ## x (mid - 1) * r_sc);\
- \
- if (m < temp)\
- { \
- ubound = mid;\
- if (ubound == lbound + 1)\
- break;\
- high = mid - 1;\
- }\
- else if (m > temp)\
- { \
- lbound = mid;\
- if (ubound == lbound + 1)\
- { \
- mid ++;\
- break;\
- }\
- low = mid + 1;\
- }\
- else\
- {\
- mid++;\
- break;\
- }\
- }\
- \
- nb = e <= 0 ? 4 : e + 2;\
- nb = nb >= w ? w - 1 : nb;\
- newf.format = FMT_F;\
- newf.u.real.w = w - nb;\
- newf.u.real.d = m == 0.0 ? d - 1 : -(mid - d - 1) ;\
- dtp->u.p.scale_factor = 0;\
- precision = determine_precision (dtp, &newf, x); \
- nprinted = FDTOA(y,precision,m); \
- \
- finish:\
- result = output_float (dtp, &newf, buffer, size, nprinted, precision,\
- sign_bit, zero_flag);\
- dtp->u.p.scale_factor = save_scale_factor;\
- \
- \
- if (nb > 0 && !dtp->u.p.g0_no_blanks)\
- {\
- p = write_block (dtp, nb);\
- if (p == NULL)\
- return;\
- if (!result)\
- pad = '*';\
- if (unlikely (is_char4_unit (dtp)))\
- {\
- gfc_char4_t *p4 = (gfc_char4_t *) p;\
- memset4 (p4, pad, nb);\
- }\
- else \
- memset (p, pad, nb);\
- }\
- }\
- OUTPUT_FLOAT_FMT_G(4,)
- OUTPUT_FLOAT_FMT_G(8,)
- #ifdef HAVE_GFC_REAL_10
- OUTPUT_FLOAT_FMT_G(10,L)
- #endif
- #ifdef HAVE_GFC_REAL_16
- # ifdef GFC_REAL_16_IS_FLOAT128
- OUTPUT_FLOAT_FMT_G(16,Q)
- #else
- OUTPUT_FLOAT_FMT_G(16,L)
- #endif
- #endif
- #undef OUTPUT_FLOAT_FMT_G
- /* EN format is tricky since the number of significant digits depends
- on the magnitude. Solve it by first printing a temporary value and
- figure out the number of significant digits from the printed
- exponent. Values y, 0.95*10.0**e <= y <10.0**e, are rounded to
- 10.0**e even when the final result will not be rounded to 10.0**e.
- For these values the exponent returned by atoi has to be decremented
- by one. The values y in the ranges
- (1000.0-0.5*10.0**(-d))*10.0**(3*n) <= y < 10.0*(3*(n+1))
- (100.0-0.5*10.0**(-d))*10.0**(3*n) <= y < 10.0*(3*n+2)
- (10.0-0.5*10.0**(-d))*10.0**(3*n) <= y < 10.0*(3*n+1)
- are correctly rounded respectively to 1.0...0*10.0*(3*(n+1)),
- 100.0...0*10.0*(3*n), and 10.0...0*10.0*(3*n), where 0...0
- represents d zeroes, by the lines 279 to 297. */
- #define EN_PREC(x,y)\
- {\
- volatile GFC_REAL_ ## x tmp, one = 1.0;\
- tmp = * (GFC_REAL_ ## x *)source;\
- if (ISFINITE (y,tmp))\
- {\
- nprinted = DTOA(y,0,tmp);\
- int e = atoi (&buffer[4]);\
- if (buffer[1] == '1')\
- {\
- tmp = (calculate_exp_ ## x (-e)) * tmp;\
- tmp = one - (tmp < 0 ? -tmp : tmp); \
- if (tmp > 0)\
- e = e - 1;\
- }\
- nbefore = e%3;\
- if (nbefore < 0)\
- nbefore = 3 + nbefore;\
- }\
- else\
- nprinted = -1;\
- }\
- static int
- determine_en_precision (st_parameter_dt *dtp, const fnode *f,
- const char *source, int len)
- {
- int nprinted;
- char buffer[10];
- const size_t size = 10;
- int nbefore; /* digits before decimal point - 1. */
- switch (len)
- {
- case 4:
- EN_PREC(4,)
- break;
- case 8:
- EN_PREC(8,)
- break;
- #ifdef HAVE_GFC_REAL_10
- case 10:
- EN_PREC(10,L)
- break;
- #endif
- #ifdef HAVE_GFC_REAL_16
- case 16:
- # ifdef GFC_REAL_16_IS_FLOAT128
- EN_PREC(16,Q)
- # else
- EN_PREC(16,L)
- # endif
- break;
- #endif
- default:
- internal_error (NULL, "bad real kind");
- }
- if (nprinted == -1)
- return -1;
- int prec = f->u.real.d + nbefore;
- if (dtp->u.p.current_unit->round_status != ROUND_UNSPECIFIED
- && dtp->u.p.current_unit->round_status != ROUND_PROCDEFINED)
- prec += 2 * len + 4;
- return prec;
- }
-
- #define WRITE_FLOAT(x,y)\
- {\
- GFC_REAL_ ## x tmp;\
- tmp = * (GFC_REAL_ ## x *)source;\
- sign_bit = SIGNBIT (y,tmp);\
- if (!ISFINITE (y,tmp))\
- { \
- write_infnan (dtp, f, ISNAN (y,tmp), sign_bit);\
- return;\
- }\
- tmp = sign_bit ? -tmp : tmp;\
- zero_flag = (tmp == 0.0);\
- if (f->format == FMT_G)\
- output_float_FMT_G_ ## x (dtp, f, tmp, buffer, size, sign_bit, \
- zero_flag, comp_d);\
- else\
- {\
- if (f->format == FMT_F)\
- nprinted = FDTOA(y,precision,tmp); \
- else\
- nprinted = DTOA(y,precision,tmp); \
- output_float (dtp, f, buffer, size, nprinted, precision,\
- sign_bit, zero_flag);\
- }\
- }\
- /* Output a real number according to its format. */
- static void
- write_float (st_parameter_dt *dtp, const fnode *f, const char *source, \
- int len, int comp_d)
- {
- int sign_bit, nprinted;
- int precision; /* Precision for snprintf call. */
- bool zero_flag;
- if (f->format != FMT_EN)
- precision = determine_precision (dtp, f, len);
- else
- precision = determine_en_precision (dtp, f, source, len);
- /* 4932 is the maximum exponent of long double and quad precision, 3
- extra characters for the sign, the decimal point, and the
- trailing null, and finally some extra digits depending on the
- requested precision. */
- const size_t size = 4932 + 3 + precision;
- #define BUF_STACK_SZ 5000
- char buf_stack[BUF_STACK_SZ];
- char *buffer;
- if (size > BUF_STACK_SZ)
- buffer = xmalloc (size);
- else
- buffer = buf_stack;
- switch (len)
- {
- case 4:
- WRITE_FLOAT(4,)
- break;
- case 8:
- WRITE_FLOAT(8,)
- break;
- #ifdef HAVE_GFC_REAL_10
- case 10:
- WRITE_FLOAT(10,L)
- break;
- #endif
- #ifdef HAVE_GFC_REAL_16
- case 16:
- # ifdef GFC_REAL_16_IS_FLOAT128
- WRITE_FLOAT(16,Q)
- # else
- WRITE_FLOAT(16,L)
- # endif
- break;
- #endif
- default:
- internal_error (NULL, "bad real kind");
- }
- if (size > BUF_STACK_SZ)
- free (buffer);
- }
|