bug-coreutils
[Top][All Lists]
Advanced

[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

reply via email to

[Prev in Thread] Current Thread [Next in Thread]