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:
parent
79d24f2484
commit
f5e55fd604
@ -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.
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user