aboutsummaryrefslogtreecommitdiff
path: root/toys
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2017-09-09 21:35:19 -0500
committerRob Landley <rob@landley.net>2017-09-09 21:35:19 -0500
commit7f2b0ceabdf5121bb2f502e93e1ed6c738493d51 (patch)
treed79eee6d2d6659c815bab9d742ae2c5f20561fbf /toys
parent5f6850fa5eccca80cd446593a21edc462a1dfad1 (diff)
downloadtoybox-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.c61
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");