📄 write.c
字号:
/* Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Contributed by Andy Vaught Namelist output contibuted by Paul ThomasThis file is part of the GNU Fortran 95 runtime library (libgfortran).Libgfortran is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2, or (at your option)any later version.In addition to the permissions in the GNU General Public License, theFree Software Foundation gives you unlimited permission to link thecompiled version of this file into combinations with other programs,and to distribute those combinations without any restriction comingfrom the use of this file. (The General Public License restrictionsdo apply in other respects; for example, they cover modification ofthe file, and distribution when not linked into a combineexecutable.)Libgfortran is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with Libgfortran; see the file COPYING. If not, write tothe Free Software Foundation, 51 Franklin Street, Fifth Floor,Boston, MA 02110-1301, USA. */#include "config.h"#include <assert.h>#include <string.h>#include <ctype.h>#include <float.h>#include <stdio.h>#include <stdlib.h>#include "libgfortran.h"#include "io.h"#define star_fill(p, n) memset(p, '*', n)typedef enum{ SIGN_NONE, SIGN_MINUS, SIGN_PLUS }sign_t;voidwrite_a (st_parameter_dt *dtp, const fnode *f, const char *source, int len){ int wlen; char *p; wlen = f->u.string.length < 0 ? len : f->u.string.length; p = write_block (dtp, wlen); if (p == NULL) return; if (wlen < len) memcpy (p, source, wlen); else { memset (p, ' ', wlen - len); memcpy (p + wlen - len, source, len); }}static GFC_INTEGER_LARGESTextract_int (const void *p, int len){ GFC_INTEGER_LARGEST i = 0; if (p == NULL) return i; switch (len) { case 1: { GFC_INTEGER_1 tmp; memcpy ((void *) &tmp, p, len); i = tmp; } break; case 2: { GFC_INTEGER_2 tmp; memcpy ((void *) &tmp, p, len); i = tmp; } break; case 4: { GFC_INTEGER_4 tmp; memcpy ((void *) &tmp, p, len); i = tmp; } break; case 8: { GFC_INTEGER_8 tmp; memcpy ((void *) &tmp, p, len); i = tmp; } break;#ifdef HAVE_GFC_INTEGER_16 case 16: { GFC_INTEGER_16 tmp; memcpy ((void *) &tmp, p, len); i = tmp; } break;#endif default: internal_error (NULL, "bad integer kind"); } return i;}static GFC_UINTEGER_LARGESTextract_uint (const void *p, int len){ GFC_UINTEGER_LARGEST i = 0; if (p == NULL) return i; switch (len) { case 1: { GFC_INTEGER_1 tmp; memcpy ((void *) &tmp, p, len); i = (GFC_UINTEGER_1) tmp; } break; case 2: { GFC_INTEGER_2 tmp; memcpy ((void *) &tmp, p, len); i = (GFC_UINTEGER_2) tmp; } break; case 4: { GFC_INTEGER_4 tmp; memcpy ((void *) &tmp, p, len); i = (GFC_UINTEGER_4) tmp; } break; case 8: { GFC_INTEGER_8 tmp; memcpy ((void *) &tmp, p, len); i = (GFC_UINTEGER_8) tmp; } break;#ifdef HAVE_GFC_INTEGER_16 case 16: { GFC_INTEGER_16 tmp; memcpy ((void *) &tmp, p, len); i = (GFC_UINTEGER_16) tmp; } break;#endif default: internal_error (NULL, "bad integer kind"); } return i;}static GFC_REAL_LARGESTextract_real (const void *p, int len){ GFC_REAL_LARGEST i = 0; switch (len) { case 4: { GFC_REAL_4 tmp; memcpy ((void *) &tmp, p, len); i = tmp; } break; case 8: { GFC_REAL_8 tmp; memcpy ((void *) &tmp, p, len); i = tmp; } break;#ifdef HAVE_GFC_REAL_10 case 10: { GFC_REAL_10 tmp; memcpy ((void *) &tmp, p, len); i = tmp; } break;#endif#ifdef HAVE_GFC_REAL_16 case 16: { GFC_REAL_16 tmp; memcpy ((void *) &tmp, p, len); i = tmp; } break;#endif default: internal_error (NULL, "bad real kind"); } return i;}/* Given a flag that indicate if a value is negative or not, return a sign_t that gives the sign that we need to produce. */static sign_tcalculate_sign (st_parameter_dt *dtp, int negative_flag){ sign_t s = SIGN_NONE; if (negative_flag) s = SIGN_MINUS; else switch (dtp->u.p.sign_status) { case SIGN_SP: s = SIGN_PLUS; break; case SIGN_SS: s = SIGN_NONE; break; case SIGN_S: s = options.optional_plus ? SIGN_PLUS : SIGN_NONE; break; } return s;}/* Returns the value of 10**d. */static GFC_REAL_LARGESTcalculate_exp (int d){ int i; GFC_REAL_LARGEST r = 1.0; for (i = 0; i< (d >= 0 ? d : -d); i++) r *= 10; r = (d >= 0) ? r : 1.0 / r; return r;}/* Generate corresponding I/O format for FMT_G 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 */static fnode *calculate_G_format (st_parameter_dt *dtp, const fnode *f, GFC_REAL_LARGEST value, int *num_blank){ int e = f->u.real.e; int d = f->u.real.d; int w = f->u.real.w; fnode *newf; GFC_REAL_LARGEST m, exp_d; int low, high, mid; int ubound, lbound; newf = get_mem (sizeof (fnode)); /* Absolute value. */ m = (value > 0.0) ? value : -value; /* In case of the two data magnitude ranges, generate E editing, Ew.d[Ee]. */ exp_d = calculate_exp (d); if ((m > 0.0 && m < 0.1 - 0.05 / exp_d) || (m >= exp_d - 0.5 ) || ((m == 0.0) && !(compile_options.allow_std & GFC_STD_F2003))) { newf->format = FMT_E; newf->u.real.w = w; newf->u.real.d = d; newf->u.real.e = e; *num_blank = 0; return newf; } /* Use binary search to find the data magnitude range. */ mid = 0; low = 0; high = d + 1; lbound = 0; ubound = d + 1; while (low <= high) { GFC_REAL_LARGEST temp; mid = (low + high) / 2; /* 0.1 * 10**mid - 0.5 * 10**(mid-d-1) */ temp = 0.1 * calculate_exp (mid) - 0.5 * calculate_exp (mid - d - 1); 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 break; } /* Pad with blanks where the exponent would be. */ if (e < 0) *num_blank = 4; else *num_blank = e + 2; /* Generate the F editing. F(w-n).(-(mid-d-1)), n' '. */ newf->format = FMT_F; newf->u.real.w = f->u.real.w - *num_blank; /* Special case. */ if (m == 0.0) newf->u.real.d = d - 1; else newf->u.real.d = - (mid - d - 1); /* For F editing, the scale factor is ignored. */ dtp->u.p.scale_factor = 0; return newf;}/* Output a real number according to its format which is FMT_G free. */static voidoutput_float (st_parameter_dt *dtp, const fnode *f, GFC_REAL_LARGEST value){ /* This must be large enough to accurately hold any value. */ char buffer[32]; char *out; char *digits; int e; char expchar; format_token ft; int w; int d; int edigits; int ndigits; /* 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; /* Number of zeros after the decimal point, whatever the precision. */ int nzero_real; int leadzero; int nblanks; int i; sign_t sign; double abslog; ft = f->format; w = f->u.real.w; d = f->u.real.d; nzero_real = -1; /* We should always know the field width and precision. */ if (d < 0) internal_error (&dtp->common, "Unspecified precision"); /* Use sprintf to print the number in the format +D.DDDDe+ddd For an N digit exponent, this gives us (32-6)-N digits after the decimal point, plus another one before the decimal point. */ sign = calculate_sign (dtp, value < 0.0); if (value < 0) value = -value; /* Printf always prints at least two exponent digits. */ if (value == 0) edigits = 2; else {#if defined(HAVE_GFC_REAL_10) || defined(HAVE_GFC_REAL_16) abslog = fabs((double) log10l(value));#else abslog = fabs(log10(value));#endif if (abslog < 100) edigits = 2; else edigits = 1 + (int) log10(abslog); } if (ft == FMT_F || ft == FMT_EN || ((ft == FMT_D || ft == FMT_E) && dtp->u.p.scale_factor != 0)) { /* Always convert at full precision to avoid double rounding. */ ndigits = 27 - edigits; } else { /* We know the number of digits, so can let printf do the rounding for us. */ if (ft == FMT_ES) ndigits = d + 1; else ndigits = d; if (ndigits > 27 - edigits) ndigits = 27 - edigits; } /* # 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 * * 31 minimum field width * * * (ndigits-1) 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. */ sprintf (buffer, "%+-#31.*" GFC_REAL_LARGEST_FORMAT "e", ndigits - 1, value); /* Check the resulting string has punctuation in the correct places. */ if (buffer[2] != '.' || buffer[ndigits + 2] != 'e') internal_error (&dtp->common, "printf is broken"); /* Read the exponent back in. */ e = atoi (&buffer[ndigits + 3]) + 1; /* Make sure zero comes out as 0.0e0. */ if (value == 0.0) e = 0; /* Normalize the fractional component. */ buffer[2] = buffer[1]; digits = &buffer[2]; /* Figure out where to place the decimal point. */ switch (ft) { case FMT_F: nbefore = e + dtp->u.p.scale_factor; if (nbefore < 0) { nzero = -nbefore; nzero_real = nzero; if (nzero > d) nzero = d; nafter = d - nzero; nbefore = 0; } else { nzero = 0; nafter = d; } expchar = 0; break; case FMT_E: case FMT_D: i = dtp->u.p.scale_factor; if (value != 0.0) e -= i; if (i < 0) { nbefore = 0; nzero = -i; nafter = d + i; } else if (i > 0) { nbefore = i; nzero = 0; nafter = (d - i) + 1; } else /* i == 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 (value != 0.0) 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 (value != 0.0) e--; nbefore = 1; nzero = 0; nafter = d; expchar = 'E'; break; default: /* Should never happen. */ internal_error (&dtp->common, "Unexpected format token"); } /* Round the value. */ if (nbefore + nafter == 0) { ndigits = 0; if (nzero_real == d && digits[0] >= '5') { /* We rounded to zero but shouldn't have */ nzero--; nafter = 1; digits[0] = '1'; ndigits = 1; } } else if (nbefore + nafter < ndigits) { ndigits = nbefore + nafter; i = ndigits; if (digits[i] >= '5') { /* 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--;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -