Re: seq .1 .1 would mistakenly generate no output on FreeBSD 6.1

2007-11-20 Thread Paul Eggert
Jim Meyering [EMAIL PROTECTED] writes:

 It seems to have exposed a problem in gnulib's vasnprintf.c:

I think this is because the revised (core-dumping) 'seq' is calling
vasnprintf with the equivalent of printf (%.1Lf, 0.8L), whereas due
to the strange compiler settings on FreeBSD the original
(non-core-dumping) 'seq' called vasnprintf with the equivalent of
printf (%.1Lf, (long double) (double) 0.8L).

I guess that the bug lies in the printing of a long double value that
is not exactly representable as a double.


___
Bug-coreutils mailing list
Bug-coreutils@gnu.org
http://lists.gnu.org/mailman/listinfo/bug-coreutils


Re: seq .1 .1 would mistakenly generate no output on FreeBSD 6.1

2007-11-20 Thread Jim Meyering
Paul Eggert [EMAIL PROTECTED] wrote:
...
 I looked at the revised print_numbers function and found what I think
 is one or two other instances of similar problems.  I hope the
 following code will have a better chance of surviving similar problems
 in the future.  (The proposed code is a tad shorter and avoids some
 code duplication and IF_LINT stuff; that's a good sign...)

 2007-11-18  Paul Eggert  [EMAIL PROTECTED]

   * src/seq.c (print_numbers): Rewrite in an attempt to avoid the
   more-general rounding issues exposed by the previous patch.

It looked ok, so I built and ran tests on freebsd6.1.
Unfortunately, it provokes several new failures:

  Abort trap (core dumped)
  -: test float-1 failed: exit status mismatch:  expected 0, got 134
  Abort trap (core dumped)
  -: test float-5 failed: exit status mismatch:  expected 0, got 134
  Abort trap (core dumped)
  -: test float-6 failed: exit status mismatch:  expected 0, got 134
  Abort trap (core dumped)
  -: test wid-1 failed: exit status mismatch:  expected 0, got 134
  Abort trap (core dumped)
  -: test wid-2 failed: exit status mismatch:  expected 0, got 134
  Abort trap (core dumped)
  -: test eq-wid-4 failed: exit status mismatch:  expected 0, got 134

It seems to have exposed a problem in gnulib's vasnprintf.c:

  freebsd6$ gdb --args ./seq 0.8 0.1 0.9
  GNU gdb 6.6
  (gdb) r
  Starting program: /tmp/coreutils-6.9.89.27-a9805-dirty/src/seq 0.8 0.1 0.9
  0.8
  Program received signal SIGABRT, Aborted.
  0x28136363 in kill () from /lib/libc.so.6
  (gdb) up
  #1  0x28136300 in raise () from /lib/libc.so.6
  (gdb)
  #2  0x28135014 in abort () from /lib/libc.so.6
  (gdb)
  #3  0x0804d8f1 in decode_long_double (x=0.8001084202172485504,
  ep=0xbfbfe1e0, mp=0xbfbfe1d8) at vasnprintf.c:877
  877 abort ();
  (gdb) l
  872   if (!(y = 0.0L  y  1.0L))
  873 abort ();
  874   m.limbs[--i] = (hi  (GMP_LIMB_BITS / 2)) | lo;
  875 }
  876   if (!(y == 0.0L))
  877 abort ();
  878   /* Normalise.  */
  879   while (m.nlimbs  0  m.limbs[m.nlimbs - 1] == 0)
  880 m.nlimbs--;
  881   *mp = m;
  (gdb) p y
  $1 = 0.60009765625
  (gdb) p ep
  $2 = (int *) 0xbfbfe1e0
  (gdb) p *ep
  $3 = -858993460
  (gdb) up
  #4  0x0804e3ca in scale10_round_decimal_long_double (
  x=0.8001084202172485504, n=1) at vasnprintf.c:1205
  1205  void *memory = decode_long_double (x, e, m);
  (gdb) p x
  $4 = 0.8001084202172485504
  (gdb) dow
  #3  0x0804d8f1 in decode_long_double (x=0.8001084202172485504,
  ep=0xbfbfe1e0, mp=0xbfbfe1d8) at vasnprintf.c:877
  877 abort ();
  (gdb) l
  872   if (!(y = 0.0L  y  1.0L))
  873 abort ();
  874   m.limbs[--i] = (hi  (GMP_LIMB_BITS / 2)) | lo;
  875 }
  876   if (!(y == 0.0L))
  877 abort ();
  878   /* Normalise.  */
  879   while (m.nlimbs  0  m.limbs[m.nlimbs - 1] == 0)
  880 m.nlimbs--;
  881   *mp = m;
  (gdb) p m
  $5 = {
nlimbs = 2,
limbs = 0x805c050
  }
  (gdb) p *(m.limbs)@m.nlimbs
  $6 = {2576980377, 1677721}


___
Bug-coreutils mailing list
Bug-coreutils@gnu.org
http://lists.gnu.org/mailman/listinfo/bug-coreutils


Re: seq .1 .1 would mistakenly generate no output on FreeBSD 6.1

2007-11-20 Thread Jim Meyering
Paul Eggert [EMAIL PROTECTED] wrote:

 Jim Meyering [EMAIL PROTECTED] writes:

 It seems to have exposed a problem in gnulib's vasnprintf.c:

 I think this is because the revised (core-dumping) 'seq' is calling
 vasnprintf with the equivalent of printf (%.1Lf, 0.8L), whereas due
 to the strange compiler settings on FreeBSD the original
 (non-core-dumping) 'seq' called vasnprintf with the equivalent of
 printf (%.1Lf, (long double) (double) 0.8L).

 I guess that the bug lies in the printing of a long double value that
 is not exactly representable as a double.

FYI, this happens with both gcc-3.4.3 and gcc-4.2-20070307.
The latter reports

  freebsd6$ gcc -v
  Using built-in specs.
  Target: i386-unknown-freebsd6.1
  Configured with: ../gcc-4.2-20070307/configure --enable-decimal-float 
--enable-languages=c,c++ --with-gmp=/usr/local --with-mpfr=/usr/local
  Thread model: posix
  gcc version 4.2.0 20070307 (prerelease)


___
Bug-coreutils mailing list
Bug-coreutils@gnu.org
http://lists.gnu.org/mailman/listinfo/bug-coreutils


Re: seq .1 .1 would mistakenly generate no output on FreeBSD 6.1

2007-11-18 Thread Paul Eggert
Jim Meyering wrote:

  (gdb) p last
  $4 = 0.1000135525271560688
  (gdb) p x
  $5 = 0.155511151231257827

That's odd, since X was set via this assignment:

  x = first + i * step;

and I is zero, which means X should equal FIRST, and (with seq .1 .1)
FIRST should equal LAST.  All the values in question have the type
long double so there shouldn't be any rounding error here.

I suspect the problem came up because FreeBSD has what I like to call
varying width floating point.  That is, long double sometimes has
a 53-bit fraction, and sometimes a 64-bit fraction, depending on which
temporary happens to hold the long double value.  This would explain
the problem, since the closest approximation to 0.1 with a 53-bit
fraction (IEEE double) is exactly
0.155511151231257827021181583404541015625, and with a
64-bit fraction (Intel extended) it's exactly
0.100013552527156068805425093160010874271392822265625,
and these numbers match the values shown above (modulo the finite
print width of GDB).

Varying width floating point conforms to C89, but it's a real hassle
to code portably to.  For example, if you have code like this:

long double x = (something);
long double y = x;

you cannot assume that y == x.  Here's another, more-drastic example:

long double x = (something);
long double y = (something_else);
assert (x == y || x != y);

The assertion might fail, since the first use of X might have a 64-bit
fraction, but the second might have a 53-bit fraction.

I looked at the revised print_numbers function and found what I think
is one or two other instances of similar problems.  I hope the
following code will have a better chance of surviving similar problems
in the future.  (The proposed code is a tad shorter and avoids some
code duplication and IF_LINT stuff; that's a good sign...)

2007-11-18  Paul Eggert  [EMAIL PROTECTED]

* src/seq.c (print_numbers): Rewrite in an attempt to avoid the
more-general rounding issues exposed by the previous patch.

diff --git a/src/seq.c b/src/seq.c
index eec5ed5..261a44b 100644
--- a/src/seq.c
+++ b/src/seq.c
@@ -238,24 +238,32 @@ static void
 print_numbers (char const *fmt, struct layout layout,
   long double first, long double step, long double last)
 {
-  long double i;
-  long double x0 IF_LINT (= 0);
+  bool out_of_range = (step  0 ? first  last : last  first);

-  for (i = 0; /* empty */; i++)
+  if (! out_of_range)
 {
-  long double x = first + i * step;
+  long double x = first;
+  long double i;

-  if (step  0 ? x  last : last  x)
+  for (i = 1; ; i++)
{
- /* If we go one past the end, but that number prints as a
-value equal to last, 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.01 0.03 to print 0.03 instead of
-stopping at 0.02.  */
-
- if (i)
+ long double x0 = x;
+ printf (fmt, x);
+ if (out_of_range)
+   break;
+ x = first + i * step;
+ out_of_range = (step  0 ? x  last : last  x);
+
+ if (out_of_range)
{
+ /* If the number just past LAST prints as a value equal
+to LAST, and prints differently from the previous
+number, then print the number.  This avoids problems
+with rounding.  For example, with the x86 it causes
+seq 0 0.01 0.03 to print 0.03 instead
+of stopping at 0.02.  */
+
+ bool print_extra_number = false;
  long double x_val;
  char *x_str;
  int x_strlen = asprintf (x_str, fmt, x);
@@ -269,34 +277,20 @@ print_numbers (char const *fmt, struct layout layout,
  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);
-   }
+ print_extra_number = !STREQ (x0_str, x_str);
  free (x0_str);
}

  free (x_str);
+ if (! print_extra_number)
+   break;
}

- /* With floating point arithmetic, we may well reach this point
-with i == 0 and first == last.  E.g., ./seq .1 .1 on FreeBSD 6.1.
-Hence the first conjunct: don't break out of this loop when
-i == 0.  *unless* first and last themselves are out of order,
-in which case we must print nothing, e.g. for ./seq -1  */
- if (i || (0  step  last  first) || (step  0  first  last))
-   break;
+ fputs (separator, stdout);
}

- 

seq .1 .1 would mistakenly generate no output on FreeBSD 6.1

2007-11-17 Thread Jim Meyering
seq.c does this:

  for (i = 0; /* empty */; i++)
{
  long double x = first + i * step;

  if (step  0 ? x  last : last  x)

and for an invocation of ./seq .1 .1, it would print nothing
(surprise!) on FreeBSD 6.1.

Why?  Because with i == 0, it had last  x

  (gdb) p last
  $4 = 0.1000135525271560688
  (gdb) p x
  $5 = 0.155511151231257827

I fixed it like this:
[and realized while typing this that the last  first conjunct,
 while right for this test case, was wrong for e.g., seq 1 -1 3.
 Another patch below. ]

diff --git a/ChangeLog b/ChangeLog
index 0e6c87a..d563ae9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2007-11-17  Jim Meyering  [EMAIL PROTECTED]
+
+   seq .1 .1 would mistakenly generate no output on some systems
+   * NEWS: Say this.
+   * src/seq.c (print_numbers): Handle another floating point corner case.
+   This avoids failure of seq's eq-wid-7 test on FreeBSD 6.1.
+
 2007-11-16  Paul Eggert  [EMAIL PROTECTED]

Port tests/rmdir/ignore away from GNU/Linux.
diff --git a/NEWS b/NEWS
index a8434c5..11efa75 100644
--- a/NEWS
+++ b/NEWS
@@ -142,6 +142,8 @@ GNU coreutils NEWS-*- 
outline -*-
   seq would mistakenly reject some valid format strings containing %%,
   and would mistakenly accept some invalid ones. e.g., %g%% and %%g, resp.

+  seq .1 .1 would mistakenly generate no output on some systems
+
   Obsolete sort usage with an invalid ordering-option character, e.g.,
   env _POSIX2_VERSION=199209 sort +1x no longer makes sort free an
   invalid pointer [introduced in coreutils-6.5]
diff --git a/src/seq.c b/src/seq.c
index d7d2521..77d5586 100644
--- a/src/seq.c
+++ b/src/seq.c
@@ -280,7 +280,13 @@ print_numbers (char const *fmt, struct layout layout,
  free (x_str);
}

- break;
+ /* With floating point arithmetic, we may well reach this point
+with i == 0 and first == last.  E.g., ./seq .1 .1 on FreeBSD 6.1.
+Hence the first conjunct: don't break out of this loop when
+i == 0.  *unless* first and last themselves are out of order,
+in which case we must print nothing, e.g. for ./seq -1  */
+ if (i || last  first)
+   break;
}

   if (i)
--
1.5.3.5.726.g41a7a

diff --git a/ChangeLog b/ChangeLog
index d563ae9..6e6f713 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2007-11-17  Jim Meyering  [EMAIL PROTECTED]

+   Correct preceding patch.
+   * src/seq.c (print_numbers): Also handle first  last  step  0.
+   * tests/misc/seq [empty-rev]: New test for this case.
+
seq .1 .1 would mistakenly generate no output on some systems
* NEWS: Say this.
* src/seq.c (print_numbers): Handle another floating point corner case.
diff --git a/src/seq.c b/src/seq.c
index 77d5586..eec5ed5 100644
--- a/src/seq.c
+++ b/src/seq.c
@@ -285,7 +285,7 @@ print_numbers (char const *fmt, struct layout layout,
 Hence the first conjunct: don't break out of this loop when
 i == 0.  *unless* first and last themselves are out of order,
 in which case we must print nothing, e.g. for ./seq -1  */
- if (i || last  first)
+ if (i || (0  step  last  first) || (step  0  first  last))
break;
}

diff --git a/tests/misc/seq b/tests/misc/seq
index 17c8f0c..3365d95 100755
--- a/tests/misc/seq
+++ b/tests/misc/seq
@@ -42,6 +42,7 @@ my @Tests =
   (
['onearg-1',qw(10), {OUT = [(1..10)]}],
['onearg-2',qw(-1)],
+   ['empty-rev', qw(1 -1 3)],
['neg-1',   qw(-10 10 10),  {OUT = [qw(-10 0 10)]}],
# ['neg-2', qw(-.1 .1 .11), {OUT = [qw(-0.1 0.0 0.1)]}],
['neg-3',   qw(1 -1 0), {OUT = [qw(1 0)]}],
--
1.5.3.5.726.g41a7a


___
Bug-coreutils mailing list
Bug-coreutils@gnu.org
http://lists.gnu.org/mailman/listinfo/bug-coreutils