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 792410 - GDateTime new_from_iso8601 test broken in 2.55 on i386
GDateTime new_from_iso8601 test broken in 2.55 on i386
Status: RESOLVED FIXED
Product: glib
Classification: Platform
Component: general
2.55.x
Other Linux
: Normal normal
: 2.56
Assigned To: gtkdev
gtkdev
Depends on:
Blocks:
 
 
Reported: 2018-01-10 17:46 UTC by Iain Lane
Modified: 2018-01-15 12:13 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
reproducer (run on i386) (304 bytes, text/x-csrc)
2018-01-11 11:23 UTC, Iain Lane
  Details
Avoid repeated floating point multiplies with ISO 8601 parsing (1.14 KB, patch)
2018-01-11 20:47 UTC, Robert Ancell
committed Details | Review
gdatetime: Mark the usecs as volatile (1.21 KB, patch)
2018-01-12 13:14 UTC, Iain Lane
committed Details | Review

Description Iain Lane 2018-01-10 17:46:46 UTC
The fix for bug #697715 (3cfac71d09dded67485f153fa76d6f063dbb6a76) broke the new_from_iso8601 test added by Robert in 491f835c17d200ede52c823ab1566c493479cdc1 on i386.

/GDateTime/new_from_iso8601: **
GLib:ERROR:../../../../../glib/tests/gdatetime.c:723:test_GDateTime_new_from_iso8601: assertion failed ((123456) == g_date_time_get_microsecond ((dt))): (123456 == 123455)
Aborted (core dumped)
Comment 1 Philip Withnall 2018-01-11 11:10:27 UTC
Works for me on x86_64. Is this on i386 or some other architecture?

Looking at the code, we’re probably not benefiting from parsing the usec string to a gdouble in parse_iso8601_time(), then converting it back to a gint64 in g_date_time_new().

Compounding the *=0.1 multiplication factors in get_iso8601_seconds() might be causing problems as well. I haven’t done the maths to work out the point at which the floating point error sums up to more than a usec there.

Robert, can you take a look at this please?
Comment 2 Iain Lane 2018-01-11 11:23:56 UTC
Created attachment 366647 [details]
reproducer (run on i386)

Yes, just i386 (I only tested x86_64 and i386 though) - sorry, I said that in the description but should have said in the subject too.

I briefly played more. This is still broken if I eliminate new_from_iso8601:

(bionic-i386)root@bionic:~# LD_LIBRARY_PATH=/build/glib2.0-zSL511/glib2.0-2.55.1/debian/build/deb/glib/.libs/ ./a.out # 2.55.1
microsecs: 123455
(bionic-i386)root@bionic:~# ./a.out  # 2.54.1
microsecs: 123456

Trivial reproducer attached.
Comment 3 Philip Withnall 2018-01-11 11:48:04 UTC
Here’s a more trivial (hopefully) reproducer. Could you run it on i386 please (I don’t have an i386 machine).

/* gcc -o foo foo.c `pkg-config --cflags --libs glib-2.0` -Wall */
#include <glib.h>
#include <glib/gprintf.h>

int
main (void)
{
#define USEC_PER_SECOND      (G_GINT64_CONSTANT (1000000))
  gdouble seconds = 42.123456;
  gint64 usec;

  usec = seconds * USEC_PER_SECOND;
  g_printf ("usec 1: %" G_GINT64_FORMAT "\n", usec);
  if ((usec + 1) * 1e-6 <= seconds) {
    usec++;
    g_printf ("Incrementing usec\n");
  }

  usec = usec % USEC_PER_SECOND;
  g_printf ("usec 2: %" G_GINT64_FORMAT "\n", usec);

  return 0;
}

---

I get the following output on x86_64:
  usec 1: 42123456
  usec 2: 123456

I can’t upload it as an attachment because Bugzilla is giving me an error 500 when I try, for some reason. :-(
Comment 4 Iain Lane 2018-01-11 12:49:13 UTC
Yeah, that's broken. (FWIW I don't have an i386 system either - this is a chroot on my x86_64 system.)

(bionic-i386)root@bionic:/build# ./foo
usec 1: 42123455
usec 2: 123455

Something interesting going on with types / promotion / truncating / something here I guess

---

/* gcc -o foo foo.c `pkg-config --cflags --libs glib-2.0` -Wall */
#include <glib.h>
#include <glib/gprintf.h>

int
main (void)
{
#define USEC_PER_SECOND      (G_GINT64_CONSTANT (1000000))
  gdouble seconds = 42.123456;
  gdouble usecd;
  gint64 usec;
  gint useci;
  gint usect;

  usec = seconds * USEC_PER_SECOND;
  usecd = seconds * USEC_PER_SECOND;
  useci = seconds * USEC_PER_SECOND;
  usect = usecd;

  g_printf ("%" G_GINT64_FORMAT " / %f / %d / %d / %d\n",
	    usec,
	    usecd,
	    useci,
	    usect,
	    (int) (seconds * USEC_PER_SECOND));
}

---

Gives

(bionic-i386)root@bionic:/build# ./foo
42123455 / 42123456.000000 / 42123455 / 42123456 / 42123455

To be honest I don't understand where the 42123455 comes from, or why the last value is different from the penultimate...
Comment 5 Philip Withnall 2018-01-11 13:00:21 UTC
Uff, I guess I need to dig into the C spec and work this all out.

Before I do that, would you be able to compile with a different compiler on i386 (clang, for example), just to try and rule out compiler bugs in gcc?
Comment 6 Iain Lane 2018-01-11 13:08:06 UTC
(In reply to Philip Withnall from comment #5)
> Uff, I guess I need to dig into the C spec and work this all out.
> 
> Before I do that, would you be able to compile with a different compiler on
> i386 (clang, for example), just to try and rule out compiler bugs in gcc?

Oh for the love of...

(bionic-i386)root@bionic:/build# clang -o foo b.c `pkg-config --cflags --libs glib-2.0` -Wall
(bionic-i386)root@bionic:/build# ./foo
42123456 / 42123456.000000 / 42123456 / 42123456 / 42123456

/o\
Comment 7 Iain Lane 2018-01-11 14:00:05 UTC
OK, so I tried

---
#include <stdio.h>

void main()
{
	double a = 42.123456;

	unsigned long long b = a * 1000000LL;

	printf ("%llu\n", b);
}
---

and it's broken on 18.04 pre-release (gcc version 7.2.0 (Ubuntu 7.2.0-18ubuntu2)) and Rawhide (gcc version 7.2.1 20180104 (Red Hat 7.2.1-6) (GCC)), and working on clang.

Not sure if this is a compiler bug or if we're spec non-compliant.
Comment 8 Robert Ancell 2018-01-11 20:34:46 UTC
Could you see if 123456 / 1000000LL is the same on both gcc and clang? I just decoded them as a double to match the existing API, but can easily change to doing that.
Comment 9 Robert Ancell 2018-01-11 20:47:05 UTC
Created attachment 366684 [details] [review]
Avoid repeated floating point multiplies with ISO 8601 parsing

Speculative patch that avoids any rounding errors with the repeated multiplication.
Comment 10 Iain Lane 2018-01-12 09:56:08 UTC
Someone on #ubuntu-devel hinted me towards `-ffloat-store', and that "fixes" GDateTime for me - and all of the reproducers given.

Additionally the ones which avoid GLib are "fixed" by compiling with -O1 or above, but for GLib itself that isn't enough.
Comment 11 Iain Lane 2018-01-12 13:14:50 UTC
Created attachment 366727 [details] [review]
gdatetime: Mark the usecs as volatile

Maybe this? I'm a bit out of my depth, but this fixes it for me. We need it for the intermediate variable too, so I split it out.

I read

https://stackoverflow.com/questions/3206101/extended-80-bit-double-floating-point-in-x87-not-sse2-we-dont-miss-it

and this being the problem probably explains why -ffloat-store fixes it too.
Comment 12 Philip Withnall 2018-01-15 11:16:15 UTC
Review of attachment 366727 [details] [review]:

That looks like a reasonable approach to me, thanks a lot. Could you add a comment in the code about the use of `volatile`, and then we can push it?
Comment 13 Philip Withnall 2018-01-15 11:24:05 UTC
Review of attachment 366684 [details] [review]:

Looks good to me, and the tests continue to pass on x86_64 with it. Iain, would you be able to run this patch through the tests on i386 too please, if you are doing another round of i386 testing, before we push it?
Comment 14 Iain Lane 2018-01-15 12:04:54 UTC
Cheers, added a comment and pushed both after successfully testing on i386.

(I slightly messed up git-bz and didn't get the bug URL added to the latter commit, so for the record...

https://git.gnome.org/browse/glib/commit/?id=e2054240c2fc14f0a5bba432475697ab2639b176)
Comment 15 Philip Withnall 2018-01-15 12:13:26 UTC
Thanks! \o/