Re: seq .1 .1 would mistakenly generate no output on FreeBSD 6.1
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
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
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
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
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