diff options
author | Rob Landley <rob@landley.net> | 2017-09-09 21:35:19 -0500 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2017-09-09 21:35:19 -0500 |
commit | 7f2b0ceabdf5121bb2f502e93e1ed6c738493d51 (patch) | |
tree | d79eee6d2d6659c815bab9d742ae2c5f20561fbf /toys | |
parent | 5f6850fa5eccca80cd446593a21edc462a1dfad1 (diff) | |
download | toybox-7f2b0ceabdf5121bb2f502e93e1ed6c738493d51.tar.gz |
Redo/add seq precision logic.
Josh Gao hit a case where "seq 1000000 1000001" output 1e+06, and while he
was there changed several things to work like existing seq implementations.
I changed a couple back (commenting out the test cases) until somebody
came come up with a reason (or existing use case) to do it that way.
Diffstat (limited to 'toys')
-rw-r--r-- | toys/lsb/seq.c | 61 |
1 files changed, 34 insertions, 27 deletions
diff --git a/toys/lsb/seq.c b/toys/lsb/seq.c index d5a6c0d1..a8c1a4e8 100644 --- a/toys/lsb/seq.c +++ b/toys/lsb/seq.c @@ -28,6 +28,8 @@ config SEQ GLOBALS( char *sep; char *fmt; + + int precision; ) // Ensure there's one %f escape with correct attributes @@ -42,51 +44,56 @@ static void insanitize(char *f) } } +// Parse a numeric argument setting *prec to the precision of this argument. +// This reproduces the "1.234e5" precision bug from upstream. +static double parsef(char *s) +{ + char *dp = strchr(s, '.'); + + if (dp++) TT.precision = maxof(TT.precision, strcspn(dp, "eE")); + + return xstrtod(s); +} + void seq_main(void) { - double first, increment, last, dd; - char *sep_str = "\n", *fmt_str = "%g"; + double first = 1, increment = 1, last, dd; int i; - // Parse command line arguments, with appropriate defaults. - // Note that any non-numeric arguments are treated as zero. - first = increment = 1; + if (!TT.sep) TT.sep = "\n"; switch (toys.optc) { - case 3: increment = atof(toys.optargs[1]); - case 2: first = atof(*toys.optargs); - default: last = atof(toys.optargs[toys.optc-1]); + case 3: increment = parsef(toys.optargs[1]); + case 2: first = parsef(*toys.optargs); + default: last = parsef(toys.optargs[toys.optc-1]); } + // Prepare format string with appropriate precision. Can't use %g because 1e6 + if (toys.optflags & FLAG_f) insanitize(TT.fmt); + else sprintf(TT.fmt = toybuf, "%%.%df", TT.precision); + // Pad to largest width if (toys.optflags & FLAG_w) { - char *s; - int len, dot, left = 0, right = 0; + int len = 0; for (i=0; i<3; i++) { dd = (double []){first, increment, last}[i]; - - len = sprintf(toybuf, "%g", dd); - if ((s = strchr(toybuf, '.'))) { - dot = s-toybuf; - if (left<dot) left = dot; - dot = len-dot-1; - if (right<dot) right = dot; - } else if (len>left) left = len; + len = maxof(len, snprintf(0, 0, TT.fmt, dd)); } - - sprintf(fmt_str = toybuf, "%%0%d.%df", left+right+!!right, right); + sprintf(TT.fmt = toybuf, "%%0%d.%df", len, TT.precision); } - if (toys.optflags & FLAG_f) insanitize(fmt_str = TT.fmt); - if (toys.optflags & FLAG_s) sep_str = TT.sep; + + // Other implementations output nothing if increment is 0 and first > last, + // but loop forever if first < last or even first == last. We output + // nothing for all three, if you want endless output use "yes". + if (!increment) return; i = 0; - dd = first; - if (increment) for (;;) { - // avoid accumulating rounding errors from increment + for (;;) { + // Multiply to avoid accumulating rounding errors from increment. dd = first+i*increment; if ((increment<0 && dd<last) || (increment>0 && dd>last)) break; - if (i++) printf("%s", sep_str); - printf(fmt_str, dd); + if (i++) printf("%s", TT.sep); + printf(TT.fmt, dd); } if (i) printf("\n"); |