public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jakub Jelinek <jakub@redhat.com>
To: Aldy Hernandez <aldyh@redhat.com>
Cc: gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] range-op: Implement floating point multiplication fold_range [PR107569]
Date: Thu, 10 Nov 2022 20:20:06 +0100	[thread overview]
Message-ID: <Y21O5iOYd2VxxSQE@tucnak> (raw)
In-Reply-To: <7304acea-b8bb-c930-546a-db6e3b3ff96b@redhat.com>

On Thu, Nov 10, 2022 at 03:50:47PM +0100, Aldy Hernandez wrote:
> > @@ -1908,6 +1910,123 @@ class foperator_minus : public range_ope
> >     }
> >   } fop_minus;
> > +/* Wrapper around frange_arithmetics, that computes the result
> > +   if inexact rounded to both directions.  Also, if one of the
> > +   operands is +-0.0 and another +-INF, return +-0.0 rather than
> > +   NAN.  */
> 
> s/frange_arithmetics/frange_arithmetic/
> 
> Also, would you mind written a little blurb about why it's necessary not to
> compute INF*0.0 as NAN.  I assume it's because you're using it for the cross
> product and you'll set maybe_nan separately, but it's nice to spell it out.

This made me think about it some more and I'll need to play around with it
some more, perhaps the right thing is similarly to what I've attached for
division to handle special cases upfront and call frange_arithmetic only
for the safe cases.
E.g. one case which the posted foperator_mult handles pessimistically is
[0.0, 10.0] * [INF, INF].  This should be just [INF, INF] +-NAN IMHO,
because the 0.0 * INF case will result in NAN, while
nextafter (0.0, 1.0) * INF
will be already INF and everything larger as well.
I could in frange_mult be very conservative and for the 0 * INF cases
set result_lb and result_ub to [0.0, INF] range (corresponding signs
depending on the xor of sign of ops), but that would be quite pessimistic as
well.  If one has:
[0.0, 0.0] * [10.0, INF], the result should be just [0.0, 0.0] +-NAN,
because again 0.0 * INF is NAN, but 0.0 * nextafter (INF, 0.0) is already 0.0.

Note, the is_square case doesn't suffer from all of this mess, the result
is never NAN (unless operand is NAN).

> It'd be nice to have some testcases.  For example, from what I can see, the
> original integer multiplication code came with some tests in
> gcc.dg/tree-ssa/vrp13.c (commit 9983270bec0a18).  It'd be nice to have some
> sanity checks, especially because so many things can go wrong with floats.
> 
> I'll leave it to you to decide what tests to include.

I've tried following, but it suffers from various issues:
1) we don't handle __builtin_signbit (whatever) == 0 (or != 0) as guarantee
   that in the guarded code whatever has signbit 0 or 1
2) __builtin_isinf (x) > 0 is lowered to x > DBL_MAX, but unfortunately we don't
   infer from that [INF,INF] range, but [DBL_MAX, INF] range
3) what I wrote above, I think we don't handle [0, 2] * [INF, INF] right but
   due to 2) we can't see it

So, maybe for now a selftest will be better than a testcase, or
alternatively a plugin test which acts like a selftest.

/* { dg-do compile { target { ! { vax-*-* powerpc-*-*spe pdp11-*-* } } } } */
/* { dg-options "-O2 -fno-trapping-math -fno-signaling-nans -fsigned-zeros -fno-tree-fre -fno-tree-dominator-opts -fno-thread-jumps -fdump-tree-optimized" } */
/* { dg-add-options ieee } */

void
foo (double x, double y)
{
  const double inf = __builtin_inf ();
  const double minf = -inf;
  if (__builtin_isnan (x) || __builtin_isnan (y))
    return;
#define TEST(n, xl, xu, yl, yu, rl, ru, nan) \
  if ((__builtin_isinf (xl) > 0				\
       ? x > 0.0 && __builtin_isinf (x)			\
       : __builtin_isinf (xu) < 0			\
       ? x < 0.0 && __builtin_isinf (x)			\
       : x >= xl && x <= xu				\
	 && (xl != 0.0					\
	     || __builtin_signbit (xl)			\
	     || !__builtin_signbit (x))			\
	 && (xu != 0.0					\
	     || !__builtin_signbit (xu)			\
	     || __builtin_signbit (x)))			\
      && (__builtin_isinf (yl) > 0			\
	  ? y > 0.0 && __builtin_isinf (y)		\
	  : __builtin_isinf (yu) < 0			\
	  ? y < 0.0 && __builtin_isinf (y)		\
	  : y >= yl && y <= yu				\
	    && (yl != 0.0				\
		|| __builtin_signbit (yl)		\
		|| !__builtin_signbit (y))		\
	    && (yu != 0.0				\
		|| !__builtin_signbit (yu)		\
		|| __builtin_signbit (y))))		\
    {							\
      double r##n = x * y;				\
      if (nan == 2)					\
	{						\
	  if (!__builtin_isnan (r##n))			\
	    __builtin_abort ();				\
	}						\
      else if (nan == 1)				\
	{						\
	  if (!__builtin_isnan (r##n))			\
	    {						\
	      if (r##n < rl || r##n > ru)		\
		__builtin_abort ();			\
	    }						\
	}						\
      else						\
	{						\
	  if (__builtin_isnan (r##n))			\
	    __builtin_abort ();				\
	  if (r##n < rl || r##n > ru)			\
	    __builtin_abort ();				\
	}						\
    }
#define TEST2(n, xl, xu, rl, ru) \
  if (__builtin_isinf (xl) > 0				\
      ? x > 0.0 && __builtin_isinf (x)			\
      : __builtin_isinf (xu) < 0			\
      ? x < 0.0 && __builtin_isinf (x)			\
      : x >= xl && x <= xu				\
	&& (xl != 0.0					\
	    || __builtin_signbit (xl)			\
	    || !__builtin_signbit (x))			\
	&& (xu != 0.0					\
	    || !__builtin_signbit (xu)			\
	    || __builtin_signbit (x)))			\
    {							\
      double s##n = x * x;				\
      if (__builtin_isnan (s##n))			\
	__builtin_abort ();				\
      if (s##n < rl || s##n > ru)			\
	__builtin_abort ();				\
    }
  TEST (1,	2.0, 4.0,	6.0, 8.0,	12.0, 32.0, 0);
  TEST (2,	-2.0, 3.0,	-7.0, 4.0,	-21.0, 14.0, 0);
  TEST (3,	-9.0, 5.0,	8.0, 10.0,	-90.0, 50.0, 0);
  TEST (4,	-0.0, 0.0,	16.0, 32.0,	-0.0, 0.0, 0);
  TEST (5,	-0.0, 0.0,	16.0, 32.0,	-0.0, 0.0, 0);
  TEST (6,	0.0, 0.0,	16.0, 32.0,	0.0, 0.0, 0);
  TEST (7,	0.0, 0.0,	16.0, 32.0,	0.0, 0.0, 0);
  TEST (8,	-0.0, -0.0,	16.0, 32.0,	-0.0, -0.0, 0);
  TEST (9,	-0.0, -0.0,	16.0, 32.0,	-0.0, -0.0, 0);
  TEST (10,	minf, inf,	minf, inf,	minf, inf, 1);
  TEST (11,	-0.0, 0.0,	128.0, inf,	-0.0, 0.0, 1);
  TEST (12,	-0.0, 0.0,	inf, inf,	0.0, 0.0, 2);
  TEST (13,	minf, minf,	0.0, 0.0,	0.0, 0.0, 2);
  TEST (14,	0.0, 2.0,	inf, inf,	inf, inf, 1);
  TEST (15,	0.0, 2.0,	minf, minf,	minf, minf, 1);
  TEST (16,	inf, inf,	-0.0, 2.0,	minf, inf, 1);
  TEST (17,	minf, minf,	-0.0, 3.0,	minf, inf, 1);

  TEST2 (1,	2.0, 4.0,			4.0, 16.0);
  TEST2 (2,	-0.0, 0.0,			-0.0, 0.0);
  TEST2 (3,	0.0, inf,			0.0, inf);
  TEST2 (4,	inf, inf,			inf, inf);
}


	Jakub


  reply	other threads:[~2022-11-10 19:20 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-10 13:44 Jakub Jelinek
2022-11-10 14:50 ` Aldy Hernandez
2022-11-10 19:20   ` Jakub Jelinek [this message]
2022-11-11  2:06     ` Jakub Jelinek
2022-11-11  8:52     ` [PATCH] range-op, v2: " Jakub Jelinek
2022-11-11 10:01       ` [PATCH] range-op: Cleanup floating point multiplication and division " Jakub Jelinek
2022-11-11 10:12         ` Aldy Hernandez
2022-11-11 10:56           ` Jakub Jelinek
2022-11-11 14:27         ` Aldy Hernandez
2022-11-11 10:01     ` [PATCH] range-op: Implement floating point multiplication " Aldy Hernandez
2022-11-11 10:47       ` Jakub Jelinek
2022-11-11 10:59         ` Aldy Hernandez

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Y21O5iOYd2VxxSQE@tucnak \
    --to=jakub@redhat.com \
    --cc=aldyh@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).