From a0304574e96916f0444b04f301f8e5191615f800 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 23 Jun 2007 09:27:11 +0200 Subject: [PATCH] seq no longer mishandles cases like "seq 0 0.000001 0.000003", where it would not print the desired last number. * doc/coreutils.texi (seq invocation): Remove advice about workaround for seq off-by-one problem, since the bug is fixed now. Replace it with more-generic advice about rounding errors. * src/seq.c (long_double_format, print_numbers): New arg NUMERIC_FORMAT. All uses changed. --- ChangeLog | 10 ++++++++++ NEWS | 3 +++ doc/coreutils.texi | 30 ++++++------------------------ src/seq.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 59 insertions(+), 26 deletions(-) diff --git a/ChangeLog b/ChangeLog index d2cfd36..aab0bc2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2007-06-22 Paul Eggert + + * NEWS: seq no longer mishandles obvious cases like + "seq 0 0.000001 0.000003" by omitting the last output number. + * doc/coreutils.texi (seq invocation): Remove advice about workaround + for seq off-by-one problem, since the bug is fixed now. Replace + it with more-generic advice about rounding errors. + * src/seq.c (long_double_format, print_numbers): + New arg NUMERIC_FORMAT. All uses changed. + 2007-06-22 Pádraig Brady (trivial change) * tests/seq/basic: Add test cases for seq off-by-one problem. diff --git a/NEWS b/NEWS index c587fe7..a588896 100644 --- a/NEWS +++ b/NEWS @@ -48,6 +48,9 @@ GNU coreutils NEWS -*- outline -*- ln=target attribute) would mistakenly output the string "target" before the name of each symlink. [introduced in coreutils-6.0] + seq no longer mishandles obvious cases like "seq 0 0.000001 0.000003", + so workarounds like "seq 0 0.000001 0.0000031" are no longer needed. + split --line-bytes=N (-C N) no longer creates an empty file [this bug is present at least as far back as textutils-1.22 (Jan, 1997)] diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 7290ab2..42558a3 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -14052,34 +14052,16 @@ $ seq 18446744073709551616 1 18446744073709551618 18446744073709551618 @end example -Be careful when using @command{seq} with a fractional @var{increment}; -otherwise you may see surprising results. Most people would expect to -see @code{0.000003} printed as the last number in this example: +Be careful when using @command{seq} with outlandish values: otherwise +you may see surprising results, as @command{seq} uses floating point +internally. For example, on the x86 platform, where the internal +representation uses a 64-bit fraction, the command: @example -$ seq -s ' ' 0 0.000001 0.000003 -0.000000 0.000001 0.000002 +seq 1 0.0000000000000000001 1.0000000000000000009 @end example -But that doesn't happen on many systems because @command{seq} is -implemented using binary floating point arithmetic (via the C -@code{long double} type)---which means decimal fractions like @code{0.000001} -cannot be represented exactly. That in turn means some nonintuitive -conditions like @w{@code{0.000001 * 3 > 0.000003}} will end up being true. - -To work around that in the above example, use a slightly larger number as -the @var{last} value: - -@example -$ seq -s ' ' 0 0.000001 0.0000031 -0.000000 0.000001 0.000002 0.000003 -@end example - -In general, when using an @var{increment} with a fractional part, where -(@var{last} - @var{first}) / @var{increment} is (mathematically) a whole -number, specify a slightly larger (or smaller, if @var{increment} is negative) -value for @var{last} to ensure that @var{last} is the final value printed -by seq. +outputs 1.0000000000000000007 twice and skips 1.0000000000000000008. @exitstatus diff --git a/src/seq.c b/src/seq.c index c59c6b5..d5d5c53 100644 --- a/src/seq.c +++ b/src/seq.c @@ -1,5 +1,5 @@ /* seq - print sequence of numbers to standard output. - Copyright (C) 1994-2006 Free Software Foundation, Inc. + Copyright (C) 1994-2007 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -210,15 +210,53 @@ print_numbers (char const *fmt, long double first, long double step, long double last) { long double i; + long double x0 IF_LINT (= 0); for (i = 0; /* empty */; i++) { long double x = first + i * step; + if (step < 0 ? x < last : last < x) - break; + { + /* If we go one past the end, but that number prints the + same way "last" does, and prints differently from the + previous number, then print "last". This avoids problems + with rounding. For example, with the x86 it causes "seq + 0 0.000001 0.000003" to print 0.000003 instead of + stopping at 0.000002. */ + + if (i) + { + char *x_str = NULL; + char *last_str = NULL; + if (asprintf (&x_str, fmt, x) < 0 + || asprintf (&last_str, fmt, last) < 0) + xalloc_die (); + + if (STREQ (x_str, last_str)) + { + char *x0_str = NULL; + if (asprintf (&x0_str, fmt, x0) < 0) + xalloc_die (); + if (!STREQ (x0_str, x_str)) + { + fputs (separator, stdout); + fputs (x_str, stdout); + } + free (x0_str); + } + + free (x_str); + free (last_str); + } + + break; + } + if (i) fputs (separator, stdout); printf (fmt, x); + x0 = x; } if (i) -- 2.7.4