diff --git a/usr.bin/printf/printf.1 b/usr.bin/printf/printf.1 index 445c7aaf4ecd..01360a413a13 100644 --- a/usr.bin/printf/printf.1 +++ b/usr.bin/printf/printf.1 @@ -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. diff --git a/usr.bin/printf/printf.c b/usr.bin/printf/printf.c index 2dcf4f447dce..64206a4ee887 100644 --- a/usr.bin/printf/printf.c +++ b/usr.bin/printf/printf.c @@ -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;