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.
This commit is contained in:
Paul Eggert 2007-06-23 09:27:11 +02:00 committed by Jim Meyering
parent 01616c9404
commit a0304574e9
4 changed files with 59 additions and 26 deletions

View File

@ -1,3 +1,13 @@
2007-06-22 Paul Eggert <eggert@cs.ucla.edu>
* 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 <P@draigBrady.com> (trivial change)
* tests/seq/basic: Add test cases for seq off-by-one problem.

3
NEWS
View File

@ -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)]

View File

@ -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

View File

@ -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)