[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] SEQ BUG
From: |
Pádraig Brady |
Subject: |
Re: [PATCH] SEQ BUG |
Date: |
Wed, 20 Jun 2007 16:05:31 +0100 |
User-agent: |
Thunderbird 1.5.0.8 (X11/20061116) |
Pádraig Brady wrote:
> Paul Eggert wrote:
>> Pádraig Brady <address@hidden> writes:
>>
>>> OK, how about the attached patch?
>> Better, but it still has problems. For example, on my platform
>> (Debian stable with GCC 4.2.0, x86) the command
>> "seq 0.0 0.1 0.90000000000000000000" outputs something different from
>> "seq 0.0 0.1 0.9". The former stops at 0.8, the latter at 0.9.
>
> You're stretching :)
> I'll see if I can fix that,
The attached patch handles this by
only counting signficant digits from the operands.
Pádraig.
diff -Naur --exclude='*.o' coreutils/doc/coreutils.texi
coreutils.pb/doc/coreutils.texi
--- coreutils/doc/coreutils.texi 2007-06-12 07:28:45.000000000 +0000
+++ coreutils.pb/doc/coreutils.texi 2007-06-12 07:42:01.000000000 +0000
@@ -14041,35 +14041,6 @@
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:
-
address@hidden
-$ seq -s ' ' 0 0.000001 0.000003
-0.000000 0.000001 0.000002
address@hidden example
-
-But that doesn't happen on many systems because @command{seq} is
-implemented using binary floating point arithmetic (via the C
address@hidden double} type)---which means decimal fractions like
@code{0.000001}
-cannot be represented exactly. That in turn means some nonintuitive
-conditions like @address@hidden * 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:
-
address@hidden
-$ seq -s ' ' 0 0.000001 0.0000031
-0.000000 0.000001 0.000002 0.000003
address@hidden 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.
-
@exitstatus
diff -Naur --exclude='*.o' coreutils/src/Makefile.am
coreutils.pb/src/Makefile.am
--- coreutils/src/Makefile.am 2007-06-12 07:26:01.000000000 +0000
+++ coreutils.pb/src/Makefile.am 2007-06-12 06:43:29.000000000 +0000
@@ -97,7 +97,7 @@
printf_LDADD = $(LDADD) $(POW_LIB) $(LIBICONV)
# If necessary, add -lm to resolve use of pow in lib/strtod.c.
-seq_LDADD = $(LDADD) $(POW_LIB)
+seq_LDADD = $(LDADD) $(SEQ_LIBM)
# If necessary, add libraries to resolve the `pow' reference in lib/strtod.c
# and the `nanosleep' reference in lib/xnanosleep.c.
diff -Naur --exclude='*.o' coreutils/src/seq.c coreutils.pb/src/seq.c
--- coreutils/src/seq.c 2007-06-11 10:20:57.000000000 +0000
+++ coreutils.pb/src/seq.c 2007-06-20 15:00:43.000000000 +0000
@@ -21,6 +21,7 @@
#include <getopt.h>
#include <stdio.h>
#include <sys/types.h>
+#include <math.h>
#include "system.h"
#include "c-strtod.h"
@@ -118,6 +119,9 @@
/* Number of digits after the decimal point, or INT_MAX if the
number can't easily be expressed as a fixed-point number. */
int precision;
+
+ /* Number of significat digits after the decimal point */
+ int significant;
};
typedef struct operand operand;
@@ -136,23 +140,37 @@
}
ret.width = strlen (arg);
- ret.precision = INT_MAX;
+ ret.significant = ret.precision = INT_MAX;
- if (! arg[strcspn (arg, "eExX")] && isfinite (ret.value))
+ if (! arg[strcspn (arg, "xX")] && isfinite (ret.value))
{
char const *decimal_point = strchr (arg, '.');
if (! decimal_point)
- ret.precision = 0;
+ ret.significant = ret.precision = 0;
else
{
- size_t fraction_len = strlen (decimal_point + 1);
+ size_t fraction_len = strcspn (decimal_point+1, "eE");
if (fraction_len <= INT_MAX)
- ret.precision = fraction_len;
+ {
+ ret.significant = ret.precision = fraction_len;
+ const char* zeros = decimal_point + fraction_len;
+ while (*zeros-- == '0')
+ ret.significant--;
+ }
ret.width += (fraction_len == 0
? -1
: (decimal_point == arg
|| ! ISDIGIT (decimal_point[-1])));
}
+ char const *e = strchr(arg, 'e');
+ if (!e) e = strchr(arg, 'E');
+ if (e)
+ {
+ long exponent = strtol (e+1, NULL, 10);
+ ret.precision += exponent < 0 ? -exponent: 0;
+ ret.significant -= exponent;
+ ret.significant = MIN (0, ret.significant);
+ }
}
return ret;
@@ -225,6 +243,24 @@
fputs (terminator, stdout);
}
+/* Calculate adjustment to last value so that inexactness
+ in floating point representation is not significant
+ when comparing against the last value */
+static long double
+get_last_adjustment (operand first, operand step, operand last)
+{
+ int prec=0;
+ prec = (first.significant != INT_MAX ? MAX (prec, first.significant): prec);
+ prec = (step.significant != INT_MAX ? MAX (prec, step.significant) : prec);
+ prec = (last.significant != INT_MAX ? MAX (prec, last.significant) : prec);
+ if (prec)
+ {
+ long double margin = powl (10, -prec)/2;
+ return step.value >= 0 ? margin: -margin;
+ }
+ return 0; /* Integers can be exactly represented, so don't adjust */
+}
+
/* Return the default format given FIRST, STEP, and LAST. */
static char const *
get_default_format (operand first, operand step, operand last)
@@ -359,6 +395,8 @@
}
}
+ last.value += get_last_adjustment (first, step, last);
+
if (format_str != NULL && equal_width)
{
error (0, 0, _("\
diff -Naur --exclude='*.o' coreutils/tests/seq/basic
coreutils.pb/tests/seq/basic
--- coreutils/tests/seq/basic 2007-06-13 07:05:33.000000000 +0000
+++ coreutils.pb/tests/seq/basic 2007-06-20 14:33:02.000000000 +0000
@@ -49,6 +49,13 @@
['neg-3', qw(1 -1 0), {OUT => [qw(1 0)]}],
['neg-4', qw(1 -1 -1), {OUT => [qw(1 0 -1)]}],
+ ['float-1', qw(0.8 0.1 0.9), {OUT => [qw(0.8 0.9)]}],
+ ['float-2', qw(0.1 0.99 1.99), {OUT => [qw(0.10 1.09)]}],
+ ['float-3', qw(10.8 0.1 10.95), {OUT => [qw(10.8 10.9)]}],
+ ['float-4', qw(0.1 -0.1 -0.2), {OUT => [qw(0.1 0.0 -0.1 -0.2)]}],
+ ['float-5', qw(0.8 1e-1 0.9), {OUT => [qw(0.8 0.9)]}],
+ ['float-6', qw(0.8 0.1 0.90000000000000000000), {OUT => [qw(0.8 0.9)]}],
+
['eq-wid-1', qw(-w 1 -1 -1), {OUT => [qw(01 00 -1)]}],
# Prior to 2.0g, this test would fail on e.g., HPUX systems
- [PATCH] SEQ BUG, (continued)
- [PATCH] SEQ BUG, Pádraig Brady, 2007/06/08
- Re: [PATCH] SEQ BUG, Pádraig Brady, 2007/06/08
- Re: [PATCH] SEQ BUG, Andreas Schwab, 2007/06/08
- Re: [PATCH] SEQ BUG, Pádraig Brady, 2007/06/08
- [PATCH] SEQ BUG, Pádraig Brady, 2007/06/13
- Re: [PATCH] SEQ BUG, Paul Eggert, 2007/06/13
- Re: [PATCH] SEQ BUG, Pádraig Brady, 2007/06/13
- Re: [PATCH] SEQ BUG, Pádraig Brady, 2007/06/19
- Re: [PATCH] SEQ BUG, Paul Eggert, 2007/06/19
- Re: [PATCH] SEQ BUG, Pádraig Brady, 2007/06/20
- Re: [PATCH] SEQ BUG,
Pádraig Brady <=
- Re: [PATCH] SEQ BUG, Paul Eggert, 2007/06/22
- Re: [PATCH] SEQ BUG, Pádraig Brady, 2007/06/22
- Re: [PATCH] SEQ BUG, Paul Eggert, 2007/06/22
- Re: [PATCH] SEQ BUG, Jim Meyering, 2007/06/22
- Re: [PATCH] SEQ BUG, Micah Cowan, 2007/06/22
- Re: [PATCH] SEQ BUG, Paul Eggert, 2007/06/22
- Re: [PATCH] SEQ BUG, Pádraig Brady, 2007/06/22
- Re: [PATCH] SEQ BUG, Paul Eggert, 2007/06/22
- Re: [PATCH] SEQ BUG, Jim Meyering, 2007/06/23
Re: SEQ BUG, John Cowan, 2007/06/07