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
next prev parent 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).