Bug 102930 - equal values appear to be different due to missing correct rounding in libc
Summary: equal values appear to be different due to missing correct rounding in libc
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: middle-end (show other bugs)
Version: 12.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-10-25 13:43 UTC by Vincent Lefèvre
Modified: 2021-10-26 07:06 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Vincent Lefèvre 2021-10-25 13:43:26 UTC
Due to missing correct rounding in libc, expressions like sin(x) may give different values at compile time and at run time. With GCC optimization, this makes equal values appear to be different (e.g. an integer variable having two different values at the same time).

Here's a testcase, inspired by PR85957 Comment 7 (which was about the x87 extended precision).

#include <stdio.h>
#include <math.h>

#define D1 0x1.005023d32fee5p+1
#define D2 0x0.2ef652eba3771p-1

__attribute__((noinline,noclone))
static double opaque(void)
{
  return D1;
}

__attribute__((noinline,noclone))
static void t1(void)
{
  double a = D1, b = opaque();

  if (a != b) printf("uneq");

  double sa = sin(a), sb = sin(b);

  printf("t1: %a %s %a\n", sa, sa == sb ? "==" : "!=", sb);
}

__attribute__((noinline,noclone))
static void t2(void)
{
  double a = D1, b = opaque();

  if (a != b) printf("uneq");

  int ia = sin(a) + D2, ib = sin(b) + D2;

  printf("t2: %d %s %d\n", ia, ia == ib ? "==" : "!=", ib);
}

__attribute__((noinline,noclone))
static void t3(void)
{
  double a = D1, b = opaque();

  if (a != b) printf("uneq");

  int ia = sin(a) + D2, ib = sin(b) + D2;

  printf("t3: ib = %d\n", ib);
  if (ia == ib)
    printf("t3: ib = %d\n", ib);
}

int main(void)
{
  t1();
  t2();
  t3();
  return 0;
}

Compile with the following options: -O2 -lm -fdisable-tree-dom3

This gives:

t1: 0x1.d109ad145c88fp-1 == 0x1.d109ad145c88ep-1
t2: 1 == 0
t3: ib = 0
t3: ib = 1

Tested GCC versions under Debian/unstable:
* gcc-8 (Debian 8.4.0-7) 8.4.0
* gcc-9 (Debian 9.4.0-3) 9.4.0
* gcc-10 (Debian 10.3.0-11) 10.3.0
* gcc-11 (Debian 11.2.0-10) 11.2.0
* gcc (Debian 20210918-1) 12.0.0 20210918 (experimental) [master r12-3644-g7afcb534239]

If -fdisable-tree-dom3 is not provided, t1 still fails with GCC 8 to 11.
Comment 1 Vincent Lefèvre 2021-10-25 13:51:47 UTC
I forgot some information: Debian unstable currently has libc6 2.32-4, where IBM's code for correct rounding was disabled some time ago. This bug might be unreproducible with other glibc versions (or other C libraries[*]). A change of the tested function and argument should make the same kind of issue appear on other platforms.

However, I've just done a test on a Debian 10 (buster) machine (libc6 2.28-10), and the bug is reproducible on this machine.

[*] A result may be correctly rounded by chance; this completely depends on the implementation.
Comment 2 Jakub Jelinek 2021-10-25 13:57:01 UTC
I think there is nothing that can be done about this, and something like this has been there since forever.  While perhaps some double routines in glibc used to be at some point correctly roundded, many others (e.g. float or long double) never were, there is always a chance you get different results from compile time folding vs. runtime evaluation.
Comment 3 Vincent Lefèvre 2021-10-25 14:01:23 UTC
(In reply to Jakub Jelinek from comment #2)
> I think there is nothing that can be done about this, and something like
> this has been there since forever.  While perhaps some double routines in
> glibc used to be at some point correctly roundded, many others (e.g. float
> or long double) never were, there is always a chance you get different
> results from compile time folding vs. runtime evaluation.

In this case, for its optimizations, GCC shouldn't assume that these results are equal.
Comment 4 jsm-csl@polyomino.org.uk 2021-10-26 00:59:19 UTC
On Mon, 25 Oct 2021, vincent-gcc at vinc17 dot net via Gcc-bugs wrote:

> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102930
> 
> --- Comment #3 from Vincent Lefèvre <vincent-gcc at vinc17 dot net> ---
> (In reply to Jakub Jelinek from comment #2)
> > I think there is nothing that can be done about this, and something like
> > this has been there since forever.  While perhaps some double routines in
> > glibc used to be at some point correctly roundded, many others (e.g. float
> > or long double) never were, there is always a chance you get different
> > results from compile time folding vs. runtime evaluation.
> 
> In this case, for its optimizations, GCC shouldn't assume that these results
> are equal.

Indeed.  It's fine that different executions of a call to such a function 
*in the abstract machine* return different values.  But each execution of 
such a call in the abstract machine must return some particular value 
(possibly different for different executions); it's not valid to optimize 
the code in a way that's inconsistent with a single return value for each 
call to the function in the abstract machine.  (Likewise for other cases 
discussed in other bugs such as out-of-range floating-point-to-integer 
conversions.)
Comment 5 Richard Biener 2021-10-26 07:06:43 UTC
It looks like the usual equivalence issue.