After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 101874 - rounding problems in trio
rounding problems in trio
Status: RESOLVED FIXED
Product: glib
Classification: Platform
Component: general
2.2.x
Other All
: Normal normal
: ---
Assigned To: gtkdev
gtkdev
: 110684 122347 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2002-12-23 21:52 UTC by Thomas Klausner
Modified: 2011-02-18 16:07 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
alternate double formatters (4.99 KB, text/plain)
2002-12-30 01:17 UTC, Matthias Clasen
  Details
patch (2.52 KB, patch)
2003-02-17 23:42 UTC, Matthias Clasen
none Details | Review
patch (11.15 KB, patch)
2003-02-20 23:48 UTC, Matthias Clasen
none Details | Review
additional sources (9.59 KB, application/octet-stream)
2003-02-20 23:49 UTC, Matthias Clasen
  Details
new patch (15.69 KB, patch)
2003-03-10 23:20 UTC, Matthias Clasen
none Details | Review
new sources (14.39 KB, application/octet-stream)
2003-03-10 23:22 UTC, Matthias Clasen
  Details
test cases (24.14 KB, text/plain)
2003-03-15 23:07 UTC, Matthias Clasen
  Details
printf.c with error handling (3.62 KB, text/plain)
2003-04-06 23:33 UTC, Matthias Clasen
  Details
reduced set of tests (10.38 KB, text/plain)
2003-04-06 23:33 UTC, Matthias Clasen
  Details
corrected failure return values for gprintf.c (1.34 KB, patch)
2003-04-07 21:35 UTC, Matthias Clasen
none Details | Review
vasnprintf.c with %lld support (22.24 KB, text/plain)
2003-04-14 23:56 UTC, Matthias Clasen
  Details

Description Thomas Klausner 2002-12-23 21:52:40 UTC
There's no glib-2.2.0 category for bug reports... this bug report is
against glib-2.2.0.

'make check' fails one test:
** ERROR **: file strtod-test.c: line 49 (main): assertion failed: (d ==
g_ascii_strtod (g_
ascii_dtostr (buffer, sizeof (buffer), d), NULL))
aborting...
Abort trap - core dumped
FAIL: strtod-test

Details:

$ gdb .libs/strtod-test
GNU gdb 5.0nb1
Copyright 2000 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386--netbsdelf"...
(gdb) br 47
Breakpoint 1 at 0x8048cc9: file strtod-test.c, line 47.
(gdb) r
Starting program: /var/obj/devel/glib2/work/glib-2.2.0/tests/.libs/strtod-test

Breakpoint 1, main () at strtod-test.c:48
48        d = pow (2.0, -1024.1);
(gdb) n
49        g_assert (d == g_ascii_strtod (g_ascii_dtostr (buffer, sizeof
(buffer), d), NULL));
(gdb) p d
$1 = -1.7976931348623157e+308
(gdb) p g_ascii_strtod (g_ascii_dtostr (buffer, sizeof (buffer), d), NULL)
No symbol "NULL" in current context.
(gdb) p g_ascii_strtod (g_ascii_dtostr (buffer, sizeof (buffer), d), 0)
$2 = -1077954072
(gdb) p g_ascii_dtostr (buffer, sizeof (buffer), d)
$3 = -1077954096
(gdb) p g_ascii_strtod (-1077954096, 0)
$4 = -1077954072
(gdb)
Comment 1 Owen Taylor 2002-12-23 22:05:37 UTC
Without protoypes, the output of the above isn't really
interesting; it would be more useful to write a test
program that showed what the results were at every stage.
Comment 2 Thomas Klausner 2002-12-23 22:19:43 UTC
I'm not exactly sure what you're looking for. Does this help?

50        d = pow (2.0, -1024.1);
(gdb) 
51        x = g_ascii_dtostr (buffer, sizeof (buffer), d);
(gdb) 
52        e = g_ascii_strtod (x, NULL);
(gdb) 
53        g_print("%s\n", x);
(gdb) 
5.190168296483618e-309
55        g_assert (d == e);
(gdb) p d
$1 = 5.1901682964836274e-309
(gdb) p x
$2 = (gchar *) 0xbfbfb9c4 "5.190168296483618e-309"
(gdb) p e
$3 = 5.1901682964836176e-309
(gdb)
Comment 3 Havoc Pennington 2002-12-23 22:24:29 UTC
I think the strtod test is just overoptimistic, should not use == to
compare two floats.
Comment 4 Owen Taylor 2002-12-24 14:02:52 UTC
Actually, the C standard requires a perfect round trip here.

Then again, the implementation that we have in the included
Trio library almost certainly doesn't meet this requirement....

(Did GLib decide to use Trio as compiled?)
Comment 5 Thomas Klausner 2002-12-25 03:40:32 UTC
This seems to be trio's problem.
During configuration, the following test fail for NetBSD:
. checking whether printf supports positional parameters
  (which standard describes this?)
and thus trio gets used.
  
Comment 6 Owen Taylor 2002-12-25 04:28:03 UTC
Positional parameters are part of the Unix and POSIX standards.
Comment 7 Thomas Klausner 2002-12-25 04:45:27 UTC
Thanks for the pointer. I filed a bug report with NetBSD to implement
positional parameters.
Comment 8 Matthias Clasen 2002-12-25 23:05:24 UTC
Owen, I believe that C99 only requires a perfect round trip here if
the used precision is less than DECIMAL_DIG, which may be as low as
10. But admittedly, the gcc which I use here sets DECIMAL_DIG to 21,
yet trio seems to deviate already at the 14th place.

  d = pow (2.0, -1024.1);
  printf ("%.17g (glibc)\n", d);  
  g_print ("%.17g (trio)\n", d);

5.1901682964836274e-309 (glibc)
5.1901682964836177e-309 (trio)

(using %.17g here since that is what g_ascii_dtostr () uses)
Comment 9 Owen Taylor 2002-12-26 04:12:55 UTC
Note that DECIMAL_DIG isn't arbitrary, but is specified 
in terms of the number of binary digits in the widest
supported floating point type in such a way that conversion
from binary to decimal with DECIMAL_DIG digits and back
is exact.

The reason that DECIMAL_DIG is bigger than the value
that g_ascii_dtostr is using is because GCC on x86
supports a 80bit long double, so DECIMAL_DIG > DBL_DIG.

So, the C standard does require that round trip conversion
from double to decimal with DBL_DIG digits and back
is exact, what this assertion is asserting.
Comment 10 Matthias Clasen 2002-12-26 16:20:52 UTC
But DBL_DIG is only 15 here, which is less than the 17 used by
g_strtod(). 
Comment 11 Owen Taylor 2002-12-28 17:10:21 UTC
I don't recall the exact reasoning behind the 17 .. it may
have just have been being conservative. But in any case.
the C standard rules are supposed to mean that when you
printf() and then read back with >= DBL_DIGITS, you
get an exact round trip even if the decimal result for 
> DBL_DIGITS isn't exact.

Comment 12 Matthias Clasen 2002-12-29 01:20:17 UTC
I believe the wording in C99 5.2.4.2.2 only implies that the results of
printing with DBL_DIG digits and printing-reading-printing will be
identical (ie the DBL_DIG digits won't change, the actual double value
may change), but trio fails even with this interpretation.
Comment 13 Matthias Clasen 2002-12-29 01:44:48 UTC
What about using %a instead of %.17g - produces a more compact result
and is specified to be precise. It's a C99 addition, but we require
specific printf features anyway. Unfortunately, the trio
implementation of it seems to be broken as well.
Comment 14 Matthias Clasen 2002-12-30 01:16:38 UTC
Since %a is broken in trio, I spent some time investigating how one
would go about implementing the functionality. I came up with three
versions of a print_double() function producing a %a-like format. 
The first two rely on the GDoubleIEEE574 union which glib provides,
thus will work only for IEEE doubles (this would reintroduce a
dependency on IEEE FP which we got rid of when we dropped the
homegrown print_string_upper_bound() implementation). The difference
between the first two functions is that one uses long long, which the
other one avoids (don't know if we generally avoid long long in glib).
The third function tries to achieve its goal in a generic way, relying
only on C99  functionality (signbit, frexp, DBL_MANT_DIG, FLT_RADIX
and fpclassify). Theoretically, this should work for non-IEEE FP
formats (untested). Limited testing seems to show that all three
functions give round-trip equality. 
I'd like to propose that we should replace g_ascii_dtostr by a
function like this.
Comment 15 Matthias Clasen 2002-12-30 01:17:30 UTC
Created attachment 13268 [details]
alternate double formatters
Comment 16 Owen Taylor 2003-01-23 21:10:03 UTC
I don't really like the idea of changing dtostr to use %a;
I suspect people are using dtostr in contexts where readability
by things other than strtod (including humans) is a factor.

Putting on 2.4.0, since fixing the decimal conversion in
Trio is probably a major undertaking.
Comment 17 Tor Lillqvist 2003-02-16 23:36:31 UTC
Another thing that's broken in trio (tested on Windows, with both gcc 
and MSVC), is %e with more than DBL_DIG precision. For instance 
g_printf("%0.40e", 14.) produces 
1.3999999999999999111821580299874767661095e+01 . (Note that 14 is 
exactly representable in floating point, so I don't see why it 
shouldn't produce 1.4000000000000000000000000000000000000000e+01).

When read by scanf("%e"), the 1.3999...e+01 above indeed produces 
something a tiny bit less than 14. 

This causes an interesting problem in GIMP, where 
doubles are transported from/to the plug-ins by g_snprintf'ing them 
with a %0.50e format and reading with scanf ("%e")...

I haven't yet checked whether using trio's scanf would help in 
getting the roundtrip correct, but I doubt it.
 
Comment 18 Tor Lillqvist 2003-02-16 23:38:27 UTC
Changing OS to "All", trio problems aren't NetBSD specific.
Comment 19 Matthias Clasen 2003-02-16 23:41:00 UTC
Since we're using trio mainly in order to get C99 snprintf return
value semantics and positional parameters, maybe we could investigate
patching  
trio to use the system printf for floating point formatting. Don't
know how feasible that is, though.
Comment 20 Matthias Clasen 2003-02-17 23:42:04 UTC
Here is a quick implemenation of that idea, which makes strtod-test
pass  with trio. It obviously still has a problem with printing pi to
the first 1025 digits...
Comment 21 Matthias Clasen 2003-02-17 23:42:39 UTC
Created attachment 14397 [details] [review]
patch
Comment 22 Matthias Clasen 2003-02-19 08:22:19 UTC
Another idea might be to look into replacing trio by the LGPLed vasnprintf 
implemenation in gnulib (http://www.gnu.org/software/gnulib/) which seems to 
implement positional parameters and C99 snprintf semantics on top of the 
system
sprintf.
Comment 23 Matthias Clasen 2003-02-20 09:33:20 UTC
More accurate summary.
Comment 24 Matthias Clasen 2003-02-20 23:47:24 UTC
Here is a patch and tarball to replace the included trio printf
implementation by an implementation based on the vasnprintf found in
gnulib. This looks a lot cleaner than trio, passes strfunc-test and
strtod-test, and is probably more portable than trio (at least it uses
proper autoconfigury). I like it. What do you think, Owen ? 
Comment 25 Matthias Clasen 2003-02-20 23:48:23 UTC
Created attachment 14484 [details] [review]
patch
Comment 26 Matthias Clasen 2003-02-20 23:49:30 UTC
Created attachment 14485 [details]
additional sources
Comment 27 Matthias Clasen 2003-02-20 23:51:05 UTC
Forgot to mention: dumping all these macros into acinclude.m4 is
probably not the optimal way to handle them. If we use this, we should
probably patch the gnulib files to use g_malloc instead of malloc.
Comment 28 Owen Taylor 2003-03-10 17:14:06 UTC
Hmmm, I can't say I have a definitive answer, but some thoughts:

 - The gnulib code is certainly closer to what we want -
   as compatible with the system scanf() as possible, 
   no incorrect math code, no strange extra extensions.

 - The fact that it only has vasprintf() means that there
   is going to be some slowdown for printf(), sprintf(),
   fprintf(), as compared to trio, since we have to
   allocate the buffer, then throw it away. 

   But vasprintf() is usually what we want (including
   for g_printe(), g_printerr when we do output conversion)
   so probably not worth worrying about.

 - Going this direction does allow less flexibility about
   systems with truly broken printf() implementations;
   still, if we wanted to go that direction, we'd really
   want to bring in our own floating-point formatting
   implementation, GNU MP, etc. Probably better to be
   lightweight.

 - Test cases definitely would be helpful in ensuring
   confidence in swapping out the code base.

A few random thoughts from reading the code:

 - I'd rather use _g_gnulib than _gnulib as the 
   namespace prefix. 

 - We need to #define all the exported symbols in
   the gnulib/ dir into a private namespace.

 - There is a FIXME about non-C99 snprintf()
   in the code which we can deal with using our
   configure check and falling back to the sprintf()
   case where a conservatively large buffer is
   allocated.

 - I'd probably use fwrite() rather than fputs()
   in _gnulib_vfprintf().
Comment 29 Matthias Clasen 2003-03-10 23:20:25 UTC
Created attachment 14910 [details] [review]
new patch
Comment 30 Matthias Clasen 2003-03-10 23:22:47 UTC
Created attachment 14911 [details]
new sources
Comment 31 Matthias Clasen 2003-03-10 23:24:28 UTC
The new patch tries to address most of the issues you raised, except
for a testsuite.
Comment 32 Matthias Clasen 2003-03-15 23:07:49 UTC
Created attachment 15050 [details]
test cases
Comment 33 Matthias Clasen 2003-03-15 23:10:52 UTC
Here are some tests for g_snprintf. Note that the test probably
folllow C99/SUS3 a bit too closely; e.g. they need the NAN and
INFINITY C99 macros which may not be present on many machines.
Comment 34 Owen Taylor 2003-03-31 16:51:02 UTC
* printf.h doesn't handle errors.

* About the configure changes (actually an old problem):

 if test "x$ac_cv_sizeof_long_long" = "x8" &&
    test -z "$glib_cv_long_long_format" ; then
-  need_trio=yes
+  need_included_printf=yes
 fi

  Isn't this wrong on a system where both long and
  long long are 64-bit?

* Also, since the gnulib printf doesn't have code
  to handle long-long integers, perhaps this shouldn't 
  be in there at all. (This is actually a bit of
  a problem, since we are currently guaranteeing
  the ability to use long long integers with our
  printf functions.)

=== 

Looking at the tests, they look good. Some thoughts are:

 - They may be too strong to have in make check
   (that is, they test things that we don't
   rely on for correct functionality of GLib/GTK+,
   and probably don't work everywhere like %a, etc, etc.) 
   It strikes me that we need a smaller set to actually 
   run in make check to test stuff that "should work
   anywhere" and stuff that we rely upon.
 - Long double usage needs to be guarded (or
   omitted ... GLib has no long-double typedefs
   or other support.)
Comment 35 Owen Taylor 2003-03-31 18:15:15 UTC
That should be "printf.c doesn't handle errors" if it wasn't
obvious.
Comment 36 Matthias Clasen 2003-04-06 23:32:24 UTC
Since configure already fails if there is no 64bit integer type, it
should probably fail as well if the system printf doesn't support that
type. Regarding error handling, I'm not quite sure what you want to
see here. I've attached a printf.c with some g_return_val_if_fail()s,
is that what you expected ?
I'll also attach a reduced set of printf tests, omitting a lot of the
more esoteric features (long long, long double, ptrdiff_t, intmax_t, 
%a, %g, etc) and adding the glib format definitions. 
Comment 37 Matthias Clasen 2003-04-06 23:33:06 UTC
Created attachment 15519 [details]
printf.c with error handling
Comment 38 Matthias Clasen 2003-04-06 23:33:42 UTC
Created attachment 15520 [details]
reduced set of tests
Comment 39 Owen Taylor 2003-04-07 00:58:00 UTC
The question is - are there systems that we want to support
where the printf doesn't support the 64bit types.

For the type itself we can say "use GCC" for systems where
the native CC isn't up to 64 bit ints. But we don't have
that same option of printf. 

(We used to make 64-bit printf types optional - not guaranteed
to exist, but there was that guarantee in 2.2.)

For error handling in printf.c, I'm not talking about
g_return_if_fail(), but the case where printf() fails,
because the format string is unparseable or because
malloc fails.

On failure, _g_gnulib_vasnprintf returns NULL, and you
should propagate that as negative return from printf,
etc; currrently it looks like you'll just get a segfault
in most places.
Comment 40 Matthias Clasen 2003-04-07 19:18:20 UTC
Hmm, how hard would it be to implement basic %lld support for
defective systems ? 

Regarding the error handling, I see that SUS demands that printf et al
return < 0 on failure. I also note that we currently have

g_return_val_if_fail (..., 0)

throughout gprintf.c. So that should be changed to 

g_return_val_if_fail (..., -1) 

in addition to making sure the functions in printf.c return -1 if 
vasnprintf() fails for any reason.

Finally, I notice that the gnulib vasnprintf (like the glibc printf
functions) fail to fail on certain errors, e.g.

vsnprintf (buf, 5, "%4$d", "not valid") 

returns 11 and prints "-107" on my system.
Comment 41 Matthias Clasen 2003-04-07 21:35:29 UTC
Created attachment 15544 [details] [review]
corrected failure return values for gprintf.c
Comment 42 Matthias Clasen 2003-04-14 07:55:43 UTC
*** Bug 110684 has been marked as a duplicate of this bug. ***
Comment 43 Matthias Clasen 2003-04-14 23:55:14 UTC
Here is a version of vasnprintf.c which contains code to format long
long itself ifndef HAVE_LONG_LONG_FORMAT. The responsible function
print_long_long was adapted from corresponding trio code and should be
reasonable complete except for grouping, which I omitted because it
would introduce locale dependencies. We should probably warn if we meet
%'lld.

Remaining tasks: 
* Adjust configure in to define HAVE_LONG_LONG_FORMAT if system printf
understands %lld.
* Move print_long_long to a separate c file to shrink the vasnprintf.c
patch.
* Add some more %lld tests to printf-test.c above.
Comment 44 Matthias Clasen 2003-04-14 23:56:15 UTC
Created attachment 15721 [details]
vasnprintf.c with %lld support
Comment 45 Matthias Clasen 2003-04-14 23:57:39 UTC
Btw. I noticed that glib provides defines for gint64 analoga of %lld
and %llu, but not %llo, %llx or %llX. Is that intentional ?
Comment 46 Matthias Clasen 2003-06-16 14:01:39 UTC
Another problem with the guaranteed 64bit type support which I just 
noticed: in 2.2, we always define G_GINT64_FORMAT, and the 
documentation of the macro speaks about printing and scanning,


but we don't provide a g_sscanf() which is guaranteed to handle 


64bit ints.
Comment 47 Owen Taylor 2003-07-28 18:22:19 UTC
* I think part of my comments above about configure got
  lost; the  problem with:
 
  if test "x$ac_cv_sizeof_long_long" = "x8" &&
     test -z "$glib_cv_long_long_format" ; then
     need_included_printf=yes
  fi

  is simply that if you have 64-bit longs, then you don't
  need a printf format for long_long.

* For the printf error handling, instead of:

  result = _g_gnulib_vasnprintf (NULL, &length, format, args);
  g_return_val_if_fail (result != NULL, 0);
 
  I'd just do:

  result = _g_gnulib_vasnprintf (NULL, &length, format, args);
  if (!result)
    return -1;

  on the principle that the _g_gnulib_* functions should work
  as identically to what they are emulating as possible, which
  doesn't include printing warnings to standard error on error
  conditions.  Plus g_return_val_if_fail() is supposed to 
  only be for programmatic errors, and vasnprintf() can
  return NULL on out-of-memory.

* In a fairly casual look, the long-long formatting stuff
  looks reasonable ... I didn't see any problems with it.

* It would be good to send the LONG_LONG emulation stuff
  back upstream once you're happy with it. (Probably obvious)

* I don't think it would hurt to add 64-bit oxX (please
  note that G_GINT64_FORMAT is *not* necessarily equivalent
  to ll; it might be equivalent to l), if there are 
  actually equivalents on all platforms. Looking at what
  we have currently, I think you could also define a
  G_GINT64_MODIFIER and do G_GINT64_MODIFIER "o"; since
  C99 has 'll', I don't think it's necesarry to worry
  that some other variant not in this pattern will show
  up.

* I wouldn't worry to much about the scanf issue, except for 
  perhaps fixing up the docs; parsing with scanf is frequently 
  a bit dubious anyways. We should probably fix up the 
  docs and tell people to use g_ascii_strtoull. (Might not
  hurt to add g_ascii_strtoll()...)

* I'm fine with you going ahead and replacing trio in HEAD
  with this code.
Comment 48 Matthias Clasen 2003-07-28 23:02:56 UTC
Committed after fixing up the error handling a bit and adding
G_GINT{16,32,64}_MODIFIER. I'll file a separate bug about
g_ascii_strtoll().
Comment 49 Owen Taylor 2003-09-15 16:29:29 UTC
*** Bug 122347 has been marked as a duplicate of this bug. ***