Path: blob/main/contrib/arm-optimized-routines/math/test/ulp.h
48254 views
/*1* Generic functions for ULP error estimation.2*3* Copyright (c) 2019-2024, Arm Limited.4* SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception5*/67/* For each different math function type,8T(x) should add a different suffix to x.9RT(x) should add a return type specific suffix to x. */1011#ifdef NEW_RT12#undef NEW_RT1314# if USE_MPFR15static int RT(ulpscale_mpfr) (mpfr_t x, int t)16{17/* TODO: pow of 2 cases. */18if (mpfr_regular_p (x))19{20mpfr_exp_t e = mpfr_get_exp (x) - RT(prec);21if (e < RT(emin))22e = RT(emin) - 1;23if (e > RT(emax) - RT(prec))24e = RT(emax) - RT(prec);25return e;26}27if (mpfr_zero_p (x))28return RT(emin) - 1;29if (mpfr_inf_p (x))30return RT(emax) - RT(prec);31/* NaN. */32return 0;33}34# endif3536/* Difference between exact result and closest real number that37gets rounded to got, i.e. error before rounding, for a correctly38rounded result the difference is 0. */39static double RT (ulperr) (RT (float) got, const struct RT (ret) * p, int r,40int ignore_zero_sign)41{42RT(float) want = p->y;43RT(float) d;44double e;4546if (RT(asuint) (got) == RT(asuint) (want))47return 0.0;48if (isnan (got) && isnan (want))49/* Ignore sign of NaN, and signalling-ness for MPFR. */50# if USE_MPFR51return 0;52# else53return RT (issignaling) (got) == RT (issignaling) (want) ? 0 : INFINITY;54# endif55if (signbit (got) != signbit (want))56{57/* Fall through to ULP calculation if ignoring sign of zero and at58exactly one of want and got is non-zero. */59if (ignore_zero_sign && want == got)60return 0.0;61if (!ignore_zero_sign || (want != 0 && got != 0))62return INFINITY;63}64if (!isfinite (want) || !isfinite (got))65{66if (isnan (got) != isnan (want))67return INFINITY;68if (isnan (want))69return 0;70if (isinf (got))71{72got = RT(copysign) (RT(halfinf), got);73want *= 0.5f;74}75if (isinf (want))76{77want = RT(copysign) (RT(halfinf), want);78got *= 0.5f;79}80}81if (r == FE_TONEAREST)82{83// TODO: incorrect when got vs want cross a powof2 boundary84/* error = got > want85? got - want - tail ulp - 0.5 ulp86: got - want - tail ulp + 0.5 ulp. */87d = got - want;88e = d > 0 ? -p->tail - 0.5 : -p->tail + 0.5;89}90else91{92if ((r == FE_DOWNWARD && got < want) || (r == FE_UPWARD && got > want)93|| (r == FE_TOWARDZERO && fabs (got) < fabs (want)))94got = RT(nextafter) (got, want);95d = got - want;96e = -p->tail;97}98return RT(scalbn) (d, -p->ulpexp) + e;99}100101static int RT(isok) (RT(float) ygot, int exgot, RT(float) ywant, int exwant,102int exmay)103{104return RT(asuint) (ygot) == RT(asuint) (ywant)105&& ((exgot ^ exwant) & ~exmay) == 0;106}107108static int RT(isok_nofenv) (RT(float) ygot, RT(float) ywant)109{110return RT(asuint) (ygot) == RT(asuint) (ywant);111}112#endif113114static inline void T (call_fenv) (const struct fun *f, struct T (args) a,115int r, RT (float) * y, int *ex,116const struct conf *conf)117{118if (r != FE_TONEAREST)119fesetround (r);120feclearexcept (FE_ALL_EXCEPT);121*y = T (call) (f, a, conf);122*ex = fetestexcept (FE_ALL_EXCEPT);123if (r != FE_TONEAREST)124fesetround (FE_TONEAREST);125}126127static inline void T (call_nofenv) (const struct fun *f, struct T (args) a,128int r, RT (float) * y, int *ex,129const struct conf *conf)130{131if (r != FE_TONEAREST)132fesetround (r);133*y = T (call) (f, a, conf);134*ex = 0;135if (r != FE_TONEAREST)136fesetround (FE_TONEAREST);137}138139static inline int T (call_long_fenv) (const struct fun *f, struct T (args) a,140int r, struct RT (ret) * p,141RT (float) ygot, int exgot)142{143if (r != FE_TONEAREST)144fesetround (r);145feclearexcept (FE_ALL_EXCEPT);146volatile struct T(args) va = a; // TODO: barrier147a = va;148RT(double) yl = T(call_long) (f, a);149p->y = (RT(float)) yl;150volatile RT(float) vy = p->y; // TODO: barrier151(void) vy;152p->ex = fetestexcept (FE_ALL_EXCEPT);153if (r != FE_TONEAREST)154fesetround (FE_TONEAREST);155p->ex_may = FE_INEXACT;156if (RT(isok) (ygot, exgot, p->y, p->ex, p->ex_may))157return 1;158p->ulpexp = RT(ulpscale) (p->y);159if (isinf (p->y))160p->tail = RT(lscalbn) (yl - (RT(double)) 2 * RT(halfinf), -p->ulpexp);161else162p->tail = RT(lscalbn) (yl - p->y, -p->ulpexp);163if (RT(fabs) (p->y) < RT(min_normal))164{165/* TODO: subnormal result is treated as undeflow even if it's166exact since call_long may not raise inexact correctly. */167if (p->y != 0 || (p->ex & FE_INEXACT))168p->ex |= FE_UNDERFLOW | FE_INEXACT;169}170return 0;171}172static inline int T(call_long_nofenv) (const struct fun *f, struct T(args) a,173int r, struct RT(ret) * p,174RT(float) ygot, int exgot)175{176if (r != FE_TONEAREST)177fesetround (r);178RT(double) yl = T(call_long) (f, a);179p->y = (RT(float)) yl;180if (r != FE_TONEAREST)181fesetround (FE_TONEAREST);182if (RT(isok_nofenv) (ygot, p->y))183return 1;184p->ulpexp = RT(ulpscale) (p->y);185if (isinf (p->y))186p->tail = RT(lscalbn) (yl - (RT(double)) 2 * RT(halfinf), -p->ulpexp);187else188p->tail = RT(lscalbn) (yl - p->y, -p->ulpexp);189return 0;190}191192/* There are nan input args and all quiet. */193static inline int T(qnanpropagation) (struct T(args) a)194{195return T(reduce) (a, isnan, ||) && !T(reduce) (a, RT(issignaling), ||);196}197static inline RT(float) T(sum) (struct T(args) a)198{199return T(reduce) (a, , +);200}201202/* returns 1 if the got result is ok. */203static inline int T(call_mpfr_fix) (const struct fun *f, struct T(args) a,204int r_fenv, struct RT(ret) * p,205RT(float) ygot, int exgot)206{207#if USE_MPFR208int t, t2;209mpfr_rnd_t r = rmap (r_fenv);210MPFR_DECL_INIT(my, RT(prec_mpfr));211MPFR_DECL_INIT(mr, RT(prec));212MPFR_DECL_INIT(me, RT(prec_mpfr));213mpfr_clear_flags ();214t = T(call_mpfr) (my, f, a, r);215/* Double rounding. */216t2 = mpfr_set (mr, my, r);217if (t2)218t = t2;219mpfr_set_emin (RT(emin));220mpfr_set_emax (RT(emax));221t = mpfr_check_range (mr, t, r);222t = mpfr_subnormalize (mr, t, r);223mpfr_set_emax (MPFR_EMAX_DEFAULT);224mpfr_set_emin (MPFR_EMIN_DEFAULT);225p->y = mpfr_get_d (mr, r);226p->ex = t ? FE_INEXACT : 0;227p->ex_may = FE_INEXACT;228if (mpfr_underflow_p () && (p->ex & FE_INEXACT))229/* TODO: handle before and after rounding uflow cases. */230p->ex |= FE_UNDERFLOW;231if (mpfr_overflow_p ())232p->ex |= FE_OVERFLOW | FE_INEXACT;233if (mpfr_divby0_p ())234p->ex |= FE_DIVBYZERO;235//if (mpfr_erangeflag_p ())236// p->ex |= FE_INVALID;237if (!mpfr_nanflag_p () && RT(isok) (ygot, exgot, p->y, p->ex, p->ex_may))238return 1;239if (mpfr_nanflag_p () && !T(qnanpropagation) (a))240p->ex |= FE_INVALID;241p->ulpexp = RT(ulpscale_mpfr) (my, t);242if (!isfinite (p->y))243{244p->tail = 0;245if (isnan (p->y))246{247/* If an input was nan keep its sign. */248p->y = T(sum) (a);249if (!isnan (p->y))250p->y = (p->y - p->y) / (p->y - p->y);251return RT(isok) (ygot, exgot, p->y, p->ex, p->ex_may);252}253mpfr_set_si_2exp (mr, signbit (p->y) ? -1 : 1, 1024, MPFR_RNDN);254if (mpfr_cmpabs (my, mr) >= 0)255return RT(isok) (ygot, exgot, p->y, p->ex, p->ex_may);256}257mpfr_sub (me, my, mr, MPFR_RNDN);258mpfr_mul_2si (me, me, -p->ulpexp, MPFR_RNDN);259p->tail = mpfr_get_d (me, MPFR_RNDN);260return 0;261#else262abort ();263#endif264}265266static int T(cmp) (const struct fun *f, struct gen *gen,267const struct conf *conf)268{269double maxerr = 0;270uint64_t cnt = 0;271uint64_t cnt1 = 0;272uint64_t cnt2 = 0;273uint64_t cntfail = 0;274int r = conf->r;275int use_mpfr = conf->mpfr;276int fenv = conf->fenv;277278for (;;)279{280struct RT(ret) want;281struct T(args) a = T(next) (gen);282int exgot;283int exgot2;284RT(float) ygot;285RT(float) ygot2;286int fail = 0;287if (fenv)288T (call_fenv) (f, a, r, &ygot, &exgot, conf);289else290T (call_nofenv) (f, a, r, &ygot, &exgot, conf);291if (f->twice) {292secondcall = 1;293if (fenv)294T (call_fenv) (f, a, r, &ygot2, &exgot2, conf);295else296T (call_nofenv) (f, a, r, &ygot2, &exgot2, conf);297secondcall = 0;298if (RT(asuint) (ygot) != RT(asuint) (ygot2))299{300fail = 1;301cntfail++;302T(printcall) (f, a);303printf (" got %a then %a for same input\n", ygot, ygot2);304}305}306cnt++;307int ok = use_mpfr308? T(call_mpfr_fix) (f, a, r, &want, ygot, exgot)309: (fenv ? T(call_long_fenv) (f, a, r, &want, ygot, exgot)310: T(call_long_nofenv) (f, a, r, &want, ygot, exgot));311if (!ok)312{313int print = 0;314double err = RT (ulperr) (ygot, &want, r, conf->ignore_zero_sign);315double abserr = fabs (err);316// TODO: count errors below accuracy limit.317if (abserr > 0)318cnt1++;319if (abserr > 1)320cnt2++;321if (abserr > conf->errlim)322{323print = 1;324if (!fail)325{326fail = 1;327cntfail++;328}329}330if (abserr > maxerr)331{332maxerr = abserr;333if (!conf->quiet && abserr > conf->softlim)334print = 1;335}336if (print)337{338T(printcall) (f, a);339// TODO: inf ulp handling340printf (" got %a want %a %+g ulp err %g\n", ygot, want.y,341want.tail, err);342}343int diff = fenv ? exgot ^ want.ex : 0;344if (fenv && (diff & ~want.ex_may))345{346if (!fail)347{348fail = 1;349cntfail++;350}351T(printcall) (f, a);352printf (" is %a %+g ulp, got except 0x%0x", want.y, want.tail,353exgot);354if (diff & exgot)355printf (" wrongly set: 0x%x", diff & exgot);356if (diff & ~exgot)357printf (" wrongly clear: 0x%x", diff & ~exgot);358putchar ('\n');359}360}361if (cnt >= conf->n)362break;363if (!conf->quiet && cnt % 0x100000 == 0)364printf ("progress: %6.3f%% cnt %llu cnt1 %llu cnt2 %llu cntfail %llu "365"maxerr %g\n",366100.0 * cnt / conf->n, (unsigned long long) cnt,367(unsigned long long) cnt1, (unsigned long long) cnt2,368(unsigned long long) cntfail, maxerr);369}370double cc = cnt;371if (cntfail)372printf ("FAIL ");373else374printf ("PASS ");375T(printgen) (f, gen);376printf (" round %c errlim %g maxerr %g %s cnt %llu cnt1 %llu %g%% cnt2 %llu "377"%g%% cntfail %llu %g%%\n",378conf->rc, conf->errlim,379maxerr, conf->r == FE_TONEAREST ? "+0.5" : "+1.0",380(unsigned long long) cnt,381(unsigned long long) cnt1, 100.0 * cnt1 / cc,382(unsigned long long) cnt2, 100.0 * cnt2 / cc,383(unsigned long long) cntfail, 100.0 * cntfail / cc);384return !!cntfail;385}386387388