https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108742

            Bug ID: 108742
           Summary: Incorrect constant folding with (or exposed by)
                    -fexcess-precision=standard
           Product: gcc
           Version: 12.2.1
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: middle-end
          Assignee: unassigned at gcc dot gnu.org
          Reporter: matz at gcc dot gnu.org
  Target Milestone: ---

This came from a discussion about a user wondering why g++ behaved different
between GCC 12 and GCC 13, regarding equality comparisons of floating point
values on x87, i.e. -m32, which ultimately was because GCC 13 activated
-fexcess-precision=standard with -std=c++17.  Now this is not in itself
surprising, but I think something is still wrong.  From commit
98e341130f87984af07c884fea773c0bb3cc8821 (adding this to c++17):

...
    The existing testcases tweaks were for cases on i686-linux where excess
    precision breaks those tests, e.g. if we have
      double d = 4.2;
      if (d == 4.2)
    then it does the expected thing only with -fexcess-precision=fast,
    because with -fexcess-precision=standard it is actually
      double d = 4.2;
      if ((long double) d == 4.2L)
    where 4.2L is different from 4.2.
...

And indeed a simple testcase about this (for C, but it demonstrates the same
thing): 
% cat float.c
#define bool _Bool
int main()
{
  double d = 4.2;

  bool eq1 = (long double)d == (long double)4.2;
  bool eq2 = (long double)d == 4.2L;
  bool eq3 = (long double)d == (long double)(double)4.2;

  __builtin_printf ("equal: %d/%d/%d\n", eq1, eq2, eq3);
  __builtin_printf ("1: %La\n", 4.2L);
  __builtin_printf ("2: %La\n", (long double)4.2);
  __builtin_printf ("3: %La\n", (long double)(double)4.2);

  return 0;
}
% gcc -m32 -O0 -fexcess-precision=fast -o fast float.c
% gcc -m32 -O0 -fexcess-precision=standard -o standard float.c
% ./fast
equal: 1/0/1
1: 0x8.666666666666666p-1
2: 0x8.6666666666668p-1
3: 0x8.6666666666668p-1
% ./standard
equal: 0/0/1
1: 0x8.666666666666666p-1
2: 0x8.666666666666666p-1
3: 0x8.6666666666668p-1


The bug I think exists is that also with =standard the first comparison
should yield 'true' and that irrespective of the excess-prec setting result (2)
should always be the same as (3).  As the comment for the commit correctly
says:
   (long double)4.2 != 4.2L
hence, even with -fexcess-precision=standard the former should _not_ be
const-folded into the latter, but rather into the exact long double value that
corresponds to (double)4.2.  Namely:

(gdb) p/x (long double)4.2
$1 = 0x40018666666666666800
(gdb) p/x (long double)4.2L
$2 = 0x40018666666666666666

The former is the correct long double value to use for the casted double
literal, not the latter.

IOW: it's fine that -fexcess-precision=standard tries to emulate the excess
precision on the hardware by doing all arithmetics in the larger type.  But
then
also the constants need to be folded exactly as the hardware would be doing.

In still other words: I think we have a bug in our constant folders and I don't
think it has anything directly to do with -fexcess-precision=standard, but is
merely exposed by it.  The wrong value is already present in 005t.original:

  __builtin_printf ((const char *) "1: %La\n",
4.19999999999999999982652765240231929055880755186080932617e+0);
  __builtin_printf ((const char *) "2: %La\n",
4.19999999999999999982652765240231929055880755186080932617e+0);
  __builtin_printf ((const char *) "3: %La\n",
4.20000000000000017763568394002504646778106689453125e+0);

Reply via email to