📄 ftoprtf.h
字号:
/********************************************************
Copyright 1989 Software Development Systems, Inc.
All Rights Reserved.
Routine to print floating types in printf-style
formats.
********************************************************/
#include "abbr.h"
#include "prflt.h"
extern char *FTOA PROTOE((char *, int, FLT *, int, int *, int *));
/****************************************************************
Emit a floating point quantity in printf format.
put: function to emit a character (see below)
where: pointer argument for "put" (see below)
flt: pointer to the value to be converted and emitted
fmt: format: f, e, E, g, or G
pp: pointer to a structure containing parameters
returns: the number of characters emitted or -1 if a
"put" of any character fails.
The arguments "put" and "where" are used as follows:
put( c, where )
The function "put" should be declared as follows:
int put( int c, TYPE *where )
TYPE may be any data type; normally it is "char" or "FILE",
depending upon where "put" places its output. The function
"put" puts the character "c" into "where". For example if
"where" is a buffer, "c" would be appended to the end of the
buffer. "put" must of course keep track of the current buffer
position.
"put" returns its argument "c" on success, or -1 on failure.
****************************************************************/
int
FTOPRTF PROTO5( (put, where, flt, fmt, pp),
int (*put)PROTOE((int,char *)),
register char *where,
FLT *flt,
int fmt,
const struct prflt *pp ) /* structure should not be modified, hence "const" */
{
/********************************************************************
A printed floating point number is a sequence of fields:
********************************************************************/
/* PREFIX */
int lpadb = 0; /* number of blank chars to emit for padding of right-
justified numbers (if blank padding was chosen) */
int sign = 0; /* may be zero (indicating no sign), '+' or blank for
positive numbers, or '-' for negative numbers */
int lpadz = 0; /* number of zeroes to emit for zero padding of right-
justified numbers (if zero padding was chosen) */
/* CENTER */
int lsig = 0; /* number of significant digits preceding the decimal
point (always one in basic format "e") */
int lzero = 0; /* number of zeroes to be used in zero-fill immediately
preceding the decimal point */
int prtpt = 0; /* print the decimal point? 0=>no, 1=>yes */
int rzero = 0; /* number of zeroes to be used in zero-fill immediately
following the decimal point */
int rsig = 0; /* number of significant digits following the decimal
point */
int rprcz = 0; /* number of zeroes to emit for zero-fill needed to
satisfy precision ("g" alternate and "f" only) */
/* SUFFIX */
int expwid = 0; /* width (in chars) of the printed exponent */
int rpadb = 0; /* number of blank chars to emit for padding of left-
justified numbers (even if zero padding chosen) */
/********************************************************************
All fields are optional except that either "lsig" or "lzero"
must be nonzero when we're ready to print. The term "significant"
above means
a) any nonzero digit or
b) any zero appearing between nonzero digits (that are not
part of the exponent)
Formats "e" and "f" are considered "basic formats", whereas "g"
is not a basic format; all printings of "g" specifications are
converted to either "e" or "f". However use of "g" modifies the
interpretations of various option fields.
Mutually exclusive field pairs (i.e. pairs of fields which cannot
both be nonzero) are: [lpadb and lpadz], [lpadb and rpadb],
[lpadz and rpadb], [lsig and rzero], and [lzero and rzero].
If both lsig and rsig are nonzero, then neither lzero nor rzero
will be nonzero.
********************************************************************/
/********************************************************************
Various other declarations.
********************************************************************/
register int sigdigs; /* # of signif. digits */
register int expval; /* value of exponent */
register int basicF; /* basic format: 0=>e, 1=>f */
int slength; /* output string length */
int wasupper = 0; /* was "fmt" in upper case? */
/********************************************************************
Buffers & associated variables.
********************************************************************/
auto int decpt, isneg; /* arguments for FTOA below */
char conv[MAXN+1]; /* buffer for FTOA converted values */
# define EBSZ 8 /* exponent buffer size */
char ebuf[EBSZ]; /* buffer for exponent E+nnnnn */
register char *expp = 0; /* exponent pointer */
register char *convp; /* significant digit pointer */
struct prflt lp; /* local copy of "pp" for efficiency */
/************************************************************
Initialize local copy of "pp" (cannot do it above be-
cause some compilers cannot handle auto aggregate init-
ialization). We make a local copy because the structure
is accessed so frequently; therefore we don't want to
be doing an indirection with every access. Next, be sure
that the specified precision has a reasonable value.
************************************************************/
lp = *pp;
if( lp.precision < 0 ) lp.precision = 6; /* default */
/************************************************************
Convert "fmt" into a lower case version of itself
(for use in switches below) and remember whether
it was an upper case letter.
************************************************************/
if( fmt == 'E' ) { fmt = 'e'; wasupper = 1; }
else if( fmt == 'G' ) { fmt = 'g'; wasupper = 1; }
/************************************************************
Perform the conversion to ASCII according to
the chosen format.
************************************************************/
switch( fmt )
{
case 'f':
/************************************************************
Precision here is interpreted as "digits after the
decimal point". FTOA will generate that many digits
plus however many digits are needed before the point.
************************************************************/
FTOA( conv, 1, flt, lp.precision, &decpt, &isneg );
break;
case 'e':
/************************************************************
The passed-in notion of "precision" means "digits
after the decimal point", but in "e" notation we are
going to guarantee that precisely one digit appears
BEFORE the decimal point, so we must augment FTOA's
notion of how many digits to generate, and have it
count significant digits only (rather than digits
after the decimal point) since the point is "floating".
************************************************************/
FTOA( conv, 0, flt, lp.precision+1, &decpt, &isneg );
break;
case 'g':
/************************************************************
A strange mix of "f" and "e". The precision implies
"significant digits", so like "f" there is no need
to augment the precision but like "e" we must ask
for significant digits only, rather than digits after
the point. NOTE: ANSI requires that zero precision
be interpreted as a precision of one instead.
************************************************************/
if( lp.precision == 0 ) lp.precision = 1;
FTOA( conv, 0, flt, lp.precision, &decpt, &isneg );
break;
default: return -1; /* error */
}
/****************************************************************
Calculate the length of the string returned by FTOA above,
NOT COUNTING TRAILING ZEROES. Note: strchr and strlen cannot
be used in a "flt" routine, so calculate the length of the
string directly.
****************************************************************/
convp = conv;
while ( *convp ) { ++convp; }
while( (convp > conv) && (*(convp-1) == '0') ) --convp;
sigdigs = convp - conv;
/************************************************************
Determine the value of the exponent. If there are no
significant digits then the exponent and decimal
point position are zero. Otherwise the exponent is
determined by the location of the decimal point, and
the decimal point field is valid.
NOTE: "isneg" is not affected by sigdigs==0 because we
want the sign to be printed according to whether the
value being converted is negative (ANSI is somewhat vague
on this but that is one way to read it). Among other
things this lets %+5.2f of -1e-20 print as -0 instead
of +0.
************************************************************/
if( sigdigs == 0 ) { expval = 0; decpt = 0; }
else expval = decpt - 1;
/***********************************************************
Determine the basic format, which is either "e" or "f".
***********************************************************/
if( fmt == 'g' )
{
/***********************************************************
We use "e" format if the printed exponent will be less
than -4 or greater than or equal to the precision,
otherwise we use "f".
***********************************************************/
if( expval < -4 || expval >= lp.precision ) basicF = 0;
else basicF = 1;
}
else basicF = fmt == 'f';
/************************************************************
Determine the value of "sign". "isneg" is nonzero if
the number is negative. If we're not forcing a sign
and the number is not negative, we must choose between
printing a blank or no sign at all.
************************************************************/
sign = isneg? '-': '+';
if( !lp.sign && !isneg ) sign = (lp.blank? ' ': 0);
/************************************************************
Determine lsig and lzero, and establish preliminary
values for rzero and rsig.
************************************************************/
if( basicF )
{
/***************************************************
"f" format may require placing zeroes around
the decimal point.
***************************************************/
if( decpt <= 0 )
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -