summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2015-06-19 19:18:21 +0100
committerPádraig Brady <P@draigBrady.com>2015-06-21 04:16:29 +0100
commit0a279f619055cc165bb3cfa3bb737cdd28ed4d70 (patch)
tree6149ce7f474051369efba96680228e3e0b5716db /src
parent71063bc858cd927e3622b511297e66b3e13f7453 (diff)
downloadcoreutils-0a279f619055cc165bb3cfa3bb737cdd28ed4d70.tar.xz
numfmt: support user specified output precision
* src/numfmt.c (usage): Update the --format description to indicate precision is allowed. (parse_format_string): Parse a precision specification like the standard printf does. (double_to_human): Honor the precision in --to mode. * tests/misc/numfmt.pl: New tests. * doc/coreutils.texi (numfmt invocation): Mention the new feature. * NEWS: Likewise.
Diffstat (limited to 'src')
-rw-r--r--src/numfmt.c50
1 files changed, 38 insertions, 12 deletions
diff --git a/src/numfmt.c b/src/numfmt.c
index 18243dd9f..81c624aeb 100644
--- a/src/numfmt.c
+++ b/src/numfmt.c
@@ -173,6 +173,7 @@ static char *padding_buffer = NULL;
static size_t padding_buffer_size = 0;
static long int padding_width = 0;
static long int zero_padding_width = 0;
+static long int user_precision = -1;
static const char *format_str = NULL;
static char *format_str_prefix = NULL;
static char *format_str_suffix = NULL;
@@ -737,15 +738,20 @@ double_to_human (long double val, int precision,
devmsg (" scaled value to %Lf * %0.f ^ %u\n", val, scale_base, power);
/* Perform rounding. */
- int ten_or_less = 0;
- if (absld (val) < 10)
+ unsigned int power_adjust = 0;
+ if (user_precision != -1)
+ power_adjust = MIN (power * 3, user_precision);
+ else if (absld (val) < 10)
{
/* for values less than 10, we allow one decimal-point digit,
so adjust before rounding. */
- ten_or_less = 1;
- val *= 10;
+ power_adjust = 1;
}
+
+ val *= powerld (10, power_adjust);
val = simple_round (val, round);
+ val /= powerld (10, power_adjust);
+
/* two special cases after rounding:
1. a "999.99" can turn into 1000 - so scale down
2. a "9.99" can turn into 10 - so don't display decimal-point. */
@@ -754,8 +760,6 @@ double_to_human (long double val, int precision,
val /= scale_base;
power++;
}
- if (ten_or_less)
- val /= 10;
/* should "7.0" be printed as "7" ?
if removing the ".0" is preferred, enable the fourth condition. */
@@ -764,10 +768,13 @@ double_to_human (long double val, int precision,
devmsg (" after rounding, value=%Lf * %0.f ^ %u\n", val, scale_base, power);
- stpcpy (pfmt, show_decimal_point ? ".1Lf%s" : ".0Lf%s");
+ stpcpy (pfmt, ".*Lf%s");
+
+ int prec = user_precision == -1 ? show_decimal_point : user_precision;
/* buf_size - 1 used here to ensure place for possible scale_IEC_I suffix. */
- num_size = snprintf (buf, buf_size - 1, fmt, val, suffix_power_char (power));
+ num_size = snprintf (buf, buf_size - 1, fmt, val, prec,
+ suffix_power_char (power));
if (num_size < 0 || num_size >= (int) buf_size - 1)
error (EXIT_FAILURE, 0,
_("failed to prepare value '%Lf' for printing"), val);
@@ -953,6 +960,7 @@ FORMAT must be suitable for printing one floating-point argument '%f'.\n\
Optional quote (%'f) will enable --grouping (if supported by current locale).\n\
Optional width value (%10f) will pad output. Optional zero (%010f) width\n\
will zero pad the number. Optional negative values (%-10f) will left align.\n\
+Optional precision (%.1f) will override the input determined precision.\n\
"), stdout);
printf (_("\n\
@@ -996,7 +1004,6 @@ Examples:\n\
Only a limited subset of printf(3) syntax is supported.
TODO:
- support .precision
support %e %g etc. rather than just %f
NOTES:
@@ -1071,9 +1078,28 @@ parse_format_string (char const *fmt)
if (fmt[i] == '\0')
error (EXIT_FAILURE, 0, _("format %s ends in %%"), quote (fmt));
+ if (fmt[i] == '.')
+ {
+ i++;
+ errno = 0;
+ user_precision = strtol (fmt + i, &endptr, 10);
+ if (errno == ERANGE || user_precision < 0 || SIZE_MAX < user_precision
+ || isblank (fmt[i]) || fmt[i] == '+')
+ {
+ /* Note we disallow negative user_precision to be
+ consistent with printf(1). POSIX states that
+ negative precision is only supported (and ignored)
+ when used with '.*f'. glibc at least will malform
+ output when passed a direct negative precision. */
+ error (EXIT_FAILURE, 0,
+ _("invalid precision in format %s"), quote (fmt));
+ }
+ i = endptr - fmt;
+ }
+
if (fmt[i] != 'f')
error (EXIT_FAILURE, 0, _("invalid format %s,"
- " directive must be %%[0]['][-][N]f"),
+ " directive must be %%[0]['][-][N][.][N]f"),
quote (fmt));
i++;
suffix_pos = i;
@@ -1158,8 +1184,8 @@ prepare_padded_number (const long double val, size_t precision)
return 0;
}
- double_to_human (val, precision, buf, sizeof (buf), scale_to, grouping,
- round_style);
+ double_to_human (val, user_precision == -1 ? precision : user_precision, buf,
+ sizeof (buf), scale_to, grouping, round_style);
if (suffix)
strncat (buf, suffix, sizeof (buf) - strlen (buf) -1);