public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jakub Jelinek <jakub@redhat.com>
To: Michael Matz <matz@suse.de>
Cc: Aldy Hernandez <aldyh@redhat.com>,
	Richard Biener <rguenther@suse.de>,
	"Joseph S. Myers" <joseph@codesourcery.com>,
	gcc-patches@gcc.gnu.org, Siddhesh Poyarekar <siddhesh@gotplt.org>,
	Andrew MacLeod <amacleod@redhat.com>
Subject: Re: [PATCH] Add targetm.libm_function_max_error
Date: Thu, 27 Apr 2023 10:59:47 +0200	[thread overview]
Message-ID: <ZEo5g7milEBdDZ6p@tucnak> (raw)
In-Reply-To: <alpine.LSU.2.20.2304261511240.3155@wotan.suse.de>

[-- Attachment #1: Type: text/plain, Size: 3745 bytes --]

On Wed, Apr 26, 2023 at 04:10:32PM +0000, Michael Matz wrote:
> On Wed, 26 Apr 2023, Jakub Jelinek via Gcc-patches wrote:
> 
> > For glibc I've gathered data from:
> > 4) using attached ulp-tester.c (how to invoke in file comment; tested
> >    both x86_64, ppc64, ppc64le 50M pseudo-random values in all 4 rounding
> >    modes, plus on x86_64 float/double sin/cos using libmvec - see
> >    attached libmvec-wrapper.c as well)
> 
> That ulp-tester.c file as attached here is not testing what you think it 
> does.  (1) It doesn't compile as it doesn't #define the TESTS macro in the 

Oops, (1) was from a last minute change.  Initially (what was posted already
back in November) it had no libmvec support, then I wanted to try to
add libmvec-wrapper.so as a real wrapper which would implement
sinf/sin/cosf/cos using the libmvec APIs.  Unfortunately it turns out the
libmvec APIs in certain corner cases call the scalar functions and then it
obviously crashed due to endless recursion; I guess I could have some
recursion counter and use dlsym RTLD_NEXT, but just chose to use different
names and quickly adjusted the tester and only tested it with the libmvec
stuff.

> !LIBMVEC_TEST case, and (2) it almost never progresses 'm', the status 
> variable used before the random numbers start, to beyond 1: you start with 
> nextafter(0.0, 1.0), which is the smallest subnormal number (with a ERANGE 
> error, but that's ignored), and you test for equality with THIS_MIN, the 
> smallest normal (!) number, until you start incrementing 'm'.

Oops, you're right.  Obviously trying to go through all denormals
exhaustively is a bad idea, attaching adjusted ulp-tester.c, which does that
only for 8192 smallest denormals, then just power of two denormals until
minimum normalized etc.  Also, I've adjusted it such that for the non-random
values it tests both positive and negative values.
And, for IBM double double it now attempts to construct a valid IBM double
double random value rather than sometimes invalid.

With this, results are on x86_64 are still
		sqrtf	sqrt	sqrtl	sqrtf128
tonearest	0	0	0	0
upward		0	0	0	0
downward	0	0	0	0
towardzero	0	0	0	0
		sinf	sin	sinl	sinf128
tonearest	1	1	1	1
upward		1	1	3	3
downward	1	1	3	3
towardzero	1	1	2	2
libmvec near	2	3-4
		cosf	cos	cosl	cosf128
tonearest	1	1	1	1
upward		1	1	3	3
downward	1	1	3	3
towardzero	1	1	2	2
libmvec near	2	3-4
On ppc64
		sqrtf	sqrt	sqrtl
tonearest	0	0	2.5
upward		0	0	2
downward	0	0	2
towardzero	0	0	3
		sinf	sin	sinl
tonearest	1	0	inf
upward		3	1	170141112472035618950840819900504604672
downward	2	1	5285505939519009108193147419208187904
towardzero	1	1	170141102330830817125005607926878961664
		cosf	cos	cosl
tonearest	1	0	83814836763238928169050593715445301248
upward		2	1	83563811520779333270048610559903924224
downward	3	1	34088889209772606301573232603422523392
towardzero	2	1	34088889209772606301573232603422523392
On ppc64le
		sqrtf	sqrt	sqrtl
tonearest	0	0	2.5
upward		0	0	2
downward	0	0	2
towardzero	0	0	3
		sinf	sin	sinl
tonearest	1	0	inf
upward		1	1	170141112472035618950840819900504604672
downward	1	1	5285505939519009108193147419208187904
towardzero	1	1	170141102330830817125005607926878961664
		cosf	cos	cosl
tonearest	1	0	83814836763238928169050593715445301248
upward		2	1	83563811520779333270048610559903924224
downward	3	1	34088889209772606301573232603422523392
towardzero	2	1	34088889209772606301573232603422523392

I guess I'll need to look at the IBM double double sinl/cosl results,
either it is some bug in my tester or the libm functions are useless.
But appart from the MODE_COMPOSITE_P cases, I think all the numbers are
within what the patch returns.
Even the sqrtl tonearest IBM double double case is larger than the libm ulps
(2.5 vs. 1).

	Jakub

[-- Attachment #2: ulp-tester.c --]
[-- Type: text/plain, Size: 8202 bytes --]

/* gcc -g -O2 -o ulp-tester ulp-tester.c -lmpfr -lm
   for i in tonearest upward downward towardzero; do ( ./ulp-tester 50000000 $i > /tmp/$i 2>&1 & ); done
   gcc -g -O2 -DLIBMVEC_TEST -o ulp-tester2 ulp-tester.c ./libmvec-wrapper.so -lmpfr -lm
   for j in b c d e; do for i in tonearest upward downward towardzero; do ( LIBMVEC_LEVEL=$j ./ulp-tester2 50000000 $i > /tmp/${i}-${j} 2>&1 & ); done; done
 */

#ifdef THIS_TYPE
static THIS_TYPE
THIS_FUNC (ulp) (THIS_TYPE val)
{
  if (__builtin_isnormal (val))
    return THIS_FUNC (ldexp) (THIS_LIT (1.0), THIS_FUNC (ilogb) (val) - THIS_MANT_DIG + 1);
  else
    return THIS_FUNC (ldexp) (THIS_LIT (1.0), THIS_MIN_EXP - THIS_MANT_DIG);
}

static void
THIS_FUNC (test) (THIS_TYPE (*fn) (THIS_TYPE),
		  int (*mpfr_fn) (mpfr_ptr, mpfr_srcptr, mpfr_rnd_t),
		  const char *name,
		  unsigned long count)
{
  char buf1[256], buf2[256], buf3[256];
  mpfr_set_default_prec (THIS_MANT_DIG);
  mpfr_t v;
  mpfr_init2 (v, THIS_MANT_DIG);
  THIS_TYPE max_ulp = THIS_LIT (0.0);
  volatile THIS_TYPE val = THIS_LIT (0.0);
  int m = 0;
  for (unsigned long i = 0; i < count; ++i)
    {
      if (m == 0)
	m = 1;
      else if (m == 1)
	{
	  val = THIS_FUNC (nextafter) (val, THIS_LIT (1.0));
	  if (val == THIS_MIN)
	    m = 3;
	  else if (i == 16384)
	    ++m;
	}
      else if (m == 2)
	{
	  val = THIS_LIT (2.0) * val;
	  if (val >= THIS_MIN)
	    ++m;
	}
      else if (m <= 10)
	{
	  val = THIS_FUNC (nextafter) (val, THIS_LIT (1.0));
	  ++m;
	}
      else if (m == 11)
	{
	  val = THIS_LIT (1.0);
	  for (int j = 0; j < 10; j++)
	    val = THIS_FUNC (nextafter) (val, THIS_LIT (0.0));
	  ++m;
	}
      else if (m <= 32)
	{
	  val = THIS_FUNC (nextafter) (val, THIS_LIT (1.0));
	  ++m;
	}
      else if (m == 33)
	{
	  val = THIS_MAX;
	  for (int j = 0; j < 10; j++)
	    val = THIS_FUNC (nextafter) (val, THIS_LIT (0.0));
	  ++m;
	}
      else if (m <= 45)
	{
	  val = THIS_FUNC (nextafter) (val, THIS_LIT (1.0));
	  ++m;
	}
      else
	val = THIS_FUNC (get_rand) ();
      if (__builtin_isnan (val))
	continue;
      volatile THIS_TYPE val2 = val;
      do
	{
	  THIS_TYPE given = fn (val2);
	  THIS_MPFR_SET (v, val2, mrnd);
	  mpfr_fn (v, v, mrnd);
	  THIS_TYPE expected = THIS_MPFR_GET (v, mrnd);
	  if ((!__builtin_isnan (given)) != (!__builtin_isnan (expected))
	      || __builtin_isinf (given) != __builtin_isinf (expected))
	    {
	      THIS_SNPRINTF (buf1, val2);
	      THIS_SNPRINTF (buf2, given);
	      THIS_SNPRINTF (buf3, expected);
	      printf ("%s (%s) = %s rather than %s\n", name, buf1, buf2, buf3);
	    }
	  else if (!__builtin_isnan (given) && !__builtin_isinf (given))
	    {
	      THIS_TYPE this_ulp
		= THIS_FUNC (fabs) (given - expected) / THIS_FUNC (ulp) (expected);
	      if (this_ulp > max_ulp)
		max_ulp = this_ulp;
	    }
	  if (m <= 45 && !__builtin_signbit (val2))
	    {
	      val2 = -val;
	      ++i;
	    }
          else
	    break;
	}
      while (1);
    }
  printf ("%s max error %.1fulp\n", name, (float) max_ulp);
}

#undef THIS_TYPE
#undef THIS_LIT
#undef THIS_FUNC
#undef THIS_MIN_EXP
#undef THIS_MANT_DIG
#undef THIS_MIN
#undef THIS_MAX
#undef THIS_MPFR_SET
#undef THIS_MPFR_GET
#undef THIS_SNPRINTF
#else
#define _GNU_SOURCE 1
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <fenv.h>

#if defined(__FLT128_DIG__) && defined(__GLIBC_PREREQ)
#if __GLIBC_PREREQ (2, 26)
#define TEST_FLT128
#define MPFR_WANT_FLOAT128
#endif
#endif

#include <gmp.h>
#include <mpfr.h>

static long rand_n;
static int rand_c;
mpfr_rnd_t mrnd;

static uint32_t
get_rand32 (void)
{
  uint32_t ret = 0;
  if (rand_c == 0)
    {
      ret = random () & 0x7fffffff;
      rand_c = 31;
    }
  else
    ret = rand_n & (((uint32_t) 1 << rand_c) - 1);
  ret <<= (32 - rand_c);
  rand_n = random ();
  ret |= rand_n & (((uint32_t) 1 << (32 - rand_c)) - 1);
  rand_n >>= (32 - rand_c);
  rand_c = 31 - (32 - rand_c);
  return ret;
}

static uint64_t
get_rand64 (void)
{
  return (((uint64_t) get_rand32 ()) << 32) | get_rand32 ();
}

static float
get_randf (void)
{
  uint32_t i = get_rand32 ();
  float f;
  memcpy (&f, &i, sizeof (f));
  return f;
}

static double
get_rand (void)
{
  uint64_t i = get_rand64 ();
  double d;
  memcpy (&d, &i, sizeof (d));
  return d;
}

static long double
get_randl (void)
{
  long double ld;
  uint64_t i = get_rand64 ();
  memcpy (&ld, &i, sizeof (i));
  if (sizeof (long double) == 12)
    {
      uint32_t j = get_rand32 ();
      memcpy ((char *) &ld + 8, &j, sizeof (j));
    }
  else if (sizeof (long double) == 16)
    {
#if __LDBL_MANT_DIG__ == 106
      double d1, d2;
      memcpy (&d1, &i, sizeof (d1));
      uint64_t j = get_rand64 ();
      if (((j >> 52) & 0x7ff) > ((i >> 52) & 0x7ff) - 52)
	j = (j & 0x800fffffffffffffULL) | (((((i >> 52) & 0x7ff) - 54) + ((j >> 52) & 3)) << 52);
      memcpy (&d2, &j, sizeof (d2));
      ld = d1;
      ld += d2;
#else
      i = get_rand64 ();
      memcpy ((char *) &ld + 8, &i, sizeof (i));
#endif
    }
  return ld;
}

#ifdef TEST_FLT128
static long double
get_randf128 (void)
{
  _Float128 f128;
  uint64_t i = get_rand64 ();
  memcpy (&f128, &i, sizeof (i));
  i = get_rand64 ();
  memcpy ((char *) &f128 + 8, &i, sizeof (i));
  return f128;
}
#endif

#define THIS_TYPE float
#define THIS_LIT(v) v##f
#define THIS_FUNC(v) v##f
#define THIS_MIN_EXP __FLT_MIN_EXP__
#define THIS_MANT_DIG __FLT_MANT_DIG__
#define THIS_MIN __FLT_MIN__
#define THIS_MAX __FLT_MAX__
#define THIS_MPFR_SET mpfr_set_flt
#define THIS_MPFR_GET mpfr_get_flt
#define THIS_SNPRINTF(buf, x) snprintf ((buf), sizeof (buf), "%a", (x));
#include "ulp-tester.c"

#define THIS_TYPE double
#define THIS_LIT(v) v
#define THIS_FUNC(v) v
#define THIS_MIN_EXP __DBL_MIN_EXP__
#define THIS_MANT_DIG __DBL_MANT_DIG__
#define THIS_MIN __DBL_MIN__
#define THIS_MAX __DBL_MAX__
#define THIS_MPFR_SET mpfr_set_d
#define THIS_MPFR_GET mpfr_get_d
#define THIS_SNPRINTF(buf, x) snprintf ((buf), sizeof (buf), "%a", (x));
#include "ulp-tester.c"

#define THIS_TYPE long double
#define THIS_LIT(v) v##L
#define THIS_FUNC(v) v##l
#define THIS_MIN_EXP __LDBL_MIN_EXP__
#define THIS_MANT_DIG __LDBL_MANT_DIG__
#define THIS_MIN __LDBL_MIN__
#define THIS_MAX __LDBL_MAX__
#define THIS_MPFR_SET mpfr_set_ld
#define THIS_MPFR_GET mpfr_get_ld
#define THIS_SNPRINTF(buf, x) snprintf ((buf), sizeof (buf), "%La", (x));
#include "ulp-tester.c"

#ifdef TEST_FLT128
#define THIS_TYPE _Float128
#define THIS_LIT(v) v##F128
#define THIS_FUNC(v) v##f128
#define THIS_MIN_EXP __FLT128_MIN_EXP__
#define THIS_MANT_DIG __FLT128_MANT_DIG__
#define THIS_MIN __FLT128_MIN__
#define THIS_MAX __FLT128_MAX__
#define THIS_MPFR_SET mpfr_set_float128
#define THIS_MPFR_GET mpfr_get_float128
#define THIS_SNPRINTF(buf, x) strfromf128 ((buf), sizeof (buf), "%a", (x));
#include "ulp-tester.c"
#else
#define testf128(fn, mpfr_fn, name, count) do { } while (0)
#endif

int
main (int argc, const char **argv)
{
  const char *arg;
  char *endptr;
  (void) argc;
  if (argc <= 1)
    arg = "";
  else
    arg = argv[1];
  unsigned long count = strtoul (arg, &endptr, 10);
  if (endptr == arg)
    {
      fprintf (stderr, "ulp-tester number_of_iterations rnd\n");
      return 1;
    }
  const char *rnd = "tonearest";
  if (argc >= 3)
    rnd = argv[2];
  mrnd = MPFR_RNDN;
  if (strcmp (rnd, "upward") == 0)
    {
      fesetround (FE_UPWARD);
      mrnd = MPFR_RNDU;
    }
  else if (strcmp (rnd, "downward") == 0)
    {
      fesetround (FE_DOWNWARD);
      mrnd = MPFR_RNDD;
    }
  else if (strcmp (rnd, "towardzero") == 0)
    {
      fesetround (FE_TOWARDZERO);
      mrnd = MPFR_RNDZ;
    }
#ifdef LIBMVEC_TEST
  extern float wsinf (float);
  extern float wcosf (float);
  extern double wsin (double);
  extern double wcos (double);
#define TESTS(fn) \
  testf (w##fn##f, mpfr_##fn, #fn "f", count);		\
  test (w##fn, mpfr_##fn, #fn, count);
  TESTS (sin);
  TESTS (cos);
#else
#define TESTS(fn) \
  testf (fn##f, mpfr_##fn, #fn "f", count);		\
  test (fn, mpfr_##fn, #fn, count);			\
  testl (fn##l, mpfr_##fn, #fn "l", count);		\
  testf128 (fn##f128, mpfr_##fn, #fn "f128", count)
  TESTS (sqrt);
  TESTS (sin);
  TESTS (cos);
  TESTS (exp10);
#endif
}
#endif

  reply	other threads:[~2023-04-27  8:59 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-04-26 14:21 Jakub Jelinek
2023-04-26 16:10 ` Michael Matz
2023-04-27  8:59   ` Jakub Jelinek [this message]
2023-04-27 11:05     ` Jakub Jelinek
2023-04-27 14:48       ` Michael Matz
2023-04-27  7:18 ` Richard Biener
2023-04-27  9:20   ` Jakub Jelinek
2023-04-27 10:34     ` Richard Biener
2023-04-27 11:08       ` [PATCH] v2: " Jakub Jelinek
2023-04-28 11:29         ` Richard Sandiford
2023-04-28 11:39           ` Jakub Jelinek

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=ZEo5g7milEBdDZ6p@tucnak \
    --to=jakub@redhat.com \
    --cc=aldyh@redhat.com \
    --cc=amacleod@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=joseph@codesourcery.com \
    --cc=matz@suse.de \
    --cc=rguenther@suse.de \
    --cc=siddhesh@gotplt.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).