Support the L modifier for floating-point values as an extension.

When L is omitted, double precision is used, so printf(1) gives
reproducable results.  When L is specified, long double precision is
used, which may improve precision, depending on the machine.
This commit is contained in:
das 2005-03-21 08:01:09 +00:00
parent 79d24f2484
commit f5e55fd604
2 changed files with 44 additions and 6 deletions

View File

@ -197,6 +197,11 @@ A character which indicates the type of format to use (one of
.Cm diouxXfFeEgGaAcsb ) .
The uppercase formats differ from their lowercase counterparts only in
that the output of the former is entirely in uppercase.
The floating-point format specifiers
.Cm ( fFeEgGaA )
may be prefixed by an
.Cm L
to request that additional precision be used, if available.
.El
.Pp
A field width or precision may be
@ -326,6 +331,11 @@ Since the floating point numbers are translated from
.Tn ASCII
to floating-point and
then back again, floating-point precision may be lost.
(By default, the number is translated to an IEEE-754 double-precision
value before being printed.
The
.Cm L
modifier may produce additional precision, depending on the hardware platform.)
.Pp
.Tn ANSI
hexadecimal character constants were deliberately not provided.

View File

@ -91,7 +91,7 @@ static const char rcsid[] =
static int asciicode(void);
static int escape(char *, int);
static int getchr(void);
static int getdouble(double *);
static int getfloating(long double *, int);
static int getint(int *);
static int getquads(quad_t *, u_quad_t *, int);
static const char
@ -110,6 +110,7 @@ main(int argc, char *argv[])
{
static const char *skip1, *skip2;
int ch, chopped, end, fieldwidth, haveprec, havewidth, precision, rval;
int mod_ldbl;
char convch, nextch, *format, *fmt, *start;
#ifndef BUILTIN
@ -211,6 +212,27 @@ next: for (start = fmt;; ++fmt) {
return (1);
}
/*
* Look for a length modifier. POSIX doesn't have these, so
* we only support them for floating-point conversions, which
* are extensions. This is useful because the L modifier can
* be used to gain extra range and precision, while omitting
* it is more likely to produce consistent results on different
* architectures. This is not so important for integers
* because overflow is the only bad thing that can happen to
* them, but consider the command printf %a 1.1
*/
if (*fmt == 'L') {
mod_ldbl = 1;
fmt++;
if (!strchr("aAeEfFgG", *fmt)) {
warnx2("bad modifier L for %%%c", *fmt, NULL);
return (1);
}
} else {
mod_ldbl = 0;
}
convch = *fmt;
nextch = *++fmt;
*fmt = '\0';
@ -276,11 +298,14 @@ next: for (start = fmt;; ++fmt) {
case 'f': case 'F':
case 'g': case 'G':
case 'a': case 'A': {
double p;
long double p;
if (getdouble(&p))
if (getfloating(&p, mod_ldbl))
rval = 1;
PF(start, p);
if (mod_ldbl)
PF(start, p);
else
PF(start, (double)p);
break;
}
default:
@ -465,7 +490,7 @@ getquads(quad_t *qp, u_quad_t *uqp, int signedconv)
}
static int
getdouble(double *dp)
getfloating(long double *dp, int mod_ldbl)
{
char *ep;
int rval;
@ -478,7 +503,10 @@ getdouble(double *dp)
}
rval = 0;
errno = 0;
*dp = strtod(*gargv, &ep);
if (mod_ldbl)
*dp = strtold(*gargv, &ep);
else
*dp = strtod(*gargv, &ep);
if (ep == *gargv) {
warnx2("%s: expected numeric value", *gargv, NULL);
rval = 1;