131914882SAlex Richardson /* 231914882SAlex Richardson * Generic functions for ULP error estimation. 331914882SAlex Richardson * 4*f3087befSAndrew Turner * Copyright (c) 2019-2024, Arm Limited. 5072a4ba8SAndrew Turner * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception 631914882SAlex Richardson */ 731914882SAlex Richardson 831914882SAlex Richardson /* For each different math function type, 931914882SAlex Richardson T(x) should add a different suffix to x. 1031914882SAlex Richardson RT(x) should add a return type specific suffix to x. */ 1131914882SAlex Richardson 1231914882SAlex Richardson #ifdef NEW_RT 1331914882SAlex Richardson #undef NEW_RT 1431914882SAlex Richardson 1531914882SAlex Richardson # if USE_MPFR 1631914882SAlex Richardson static int RT(ulpscale_mpfr) (mpfr_t x, int t) 1731914882SAlex Richardson { 1831914882SAlex Richardson /* TODO: pow of 2 cases. */ 1931914882SAlex Richardson if (mpfr_regular_p (x)) 2031914882SAlex Richardson { 2131914882SAlex Richardson mpfr_exp_t e = mpfr_get_exp (x) - RT(prec); 2231914882SAlex Richardson if (e < RT(emin)) 2331914882SAlex Richardson e = RT(emin) - 1; 2431914882SAlex Richardson if (e > RT(emax) - RT(prec)) 2531914882SAlex Richardson e = RT(emax) - RT(prec); 2631914882SAlex Richardson return e; 2731914882SAlex Richardson } 2831914882SAlex Richardson if (mpfr_zero_p (x)) 2931914882SAlex Richardson return RT(emin) - 1; 3031914882SAlex Richardson if (mpfr_inf_p (x)) 3131914882SAlex Richardson return RT(emax) - RT(prec); 3231914882SAlex Richardson /* NaN. */ 3331914882SAlex Richardson return 0; 3431914882SAlex Richardson } 3531914882SAlex Richardson # endif 3631914882SAlex Richardson 3731914882SAlex Richardson /* Difference between exact result and closest real number that 3831914882SAlex Richardson gets rounded to got, i.e. error before rounding, for a correctly 3931914882SAlex Richardson rounded result the difference is 0. */ 405a02ffc3SAndrew Turner static double RT (ulperr) (RT (float) got, const struct RT (ret) * p, int r, 415a02ffc3SAndrew Turner int ignore_zero_sign) 4231914882SAlex Richardson { 4331914882SAlex Richardson RT(float) want = p->y; 4431914882SAlex Richardson RT(float) d; 4531914882SAlex Richardson double e; 4631914882SAlex Richardson 4731914882SAlex Richardson if (RT(asuint) (got) == RT(asuint) (want)) 4831914882SAlex Richardson return 0.0; 495a02ffc3SAndrew Turner if (isnan (got) && isnan (want)) 50*f3087befSAndrew Turner /* Ignore sign of NaN, and signalling-ness for MPFR. */ 51*f3087befSAndrew Turner # if USE_MPFR 52*f3087befSAndrew Turner return 0; 53*f3087befSAndrew Turner # else 545a02ffc3SAndrew Turner return RT (issignaling) (got) == RT (issignaling) (want) ? 0 : INFINITY; 55*f3087befSAndrew Turner # endif 5631914882SAlex Richardson if (signbit (got) != signbit (want)) 575a02ffc3SAndrew Turner { 585a02ffc3SAndrew Turner /* Fall through to ULP calculation if ignoring sign of zero and at 595a02ffc3SAndrew Turner exactly one of want and got is non-zero. */ 605a02ffc3SAndrew Turner if (ignore_zero_sign && want == got) 615a02ffc3SAndrew Turner return 0.0; 625a02ffc3SAndrew Turner if (!ignore_zero_sign || (want != 0 && got != 0)) 6331914882SAlex Richardson return INFINITY; 645a02ffc3SAndrew Turner } 6531914882SAlex Richardson if (!isfinite (want) || !isfinite (got)) 6631914882SAlex Richardson { 6731914882SAlex Richardson if (isnan (got) != isnan (want)) 6831914882SAlex Richardson return INFINITY; 6931914882SAlex Richardson if (isnan (want)) 7031914882SAlex Richardson return 0; 7131914882SAlex Richardson if (isinf (got)) 7231914882SAlex Richardson { 7331914882SAlex Richardson got = RT(copysign) (RT(halfinf), got); 7431914882SAlex Richardson want *= 0.5f; 7531914882SAlex Richardson } 7631914882SAlex Richardson if (isinf (want)) 7731914882SAlex Richardson { 7831914882SAlex Richardson want = RT(copysign) (RT(halfinf), want); 7931914882SAlex Richardson got *= 0.5f; 8031914882SAlex Richardson } 8131914882SAlex Richardson } 8231914882SAlex Richardson if (r == FE_TONEAREST) 8331914882SAlex Richardson { 8431914882SAlex Richardson // TODO: incorrect when got vs want cross a powof2 boundary 8531914882SAlex Richardson /* error = got > want 8631914882SAlex Richardson ? got - want - tail ulp - 0.5 ulp 87*f3087befSAndrew Turner : got - want - tail ulp + 0.5 ulp. */ 8831914882SAlex Richardson d = got - want; 8931914882SAlex Richardson e = d > 0 ? -p->tail - 0.5 : -p->tail + 0.5; 9031914882SAlex Richardson } 9131914882SAlex Richardson else 9231914882SAlex Richardson { 9331914882SAlex Richardson if ((r == FE_DOWNWARD && got < want) || (r == FE_UPWARD && got > want) 9431914882SAlex Richardson || (r == FE_TOWARDZERO && fabs (got) < fabs (want))) 9531914882SAlex Richardson got = RT(nextafter) (got, want); 9631914882SAlex Richardson d = got - want; 9731914882SAlex Richardson e = -p->tail; 9831914882SAlex Richardson } 9931914882SAlex Richardson return RT(scalbn) (d, -p->ulpexp) + e; 10031914882SAlex Richardson } 10131914882SAlex Richardson 10231914882SAlex Richardson static int RT(isok) (RT(float) ygot, int exgot, RT(float) ywant, int exwant, 10331914882SAlex Richardson int exmay) 10431914882SAlex Richardson { 10531914882SAlex Richardson return RT(asuint) (ygot) == RT(asuint) (ywant) 10631914882SAlex Richardson && ((exgot ^ exwant) & ~exmay) == 0; 10731914882SAlex Richardson } 10831914882SAlex Richardson 10931914882SAlex Richardson static int RT(isok_nofenv) (RT(float) ygot, RT(float) ywant) 11031914882SAlex Richardson { 11131914882SAlex Richardson return RT(asuint) (ygot) == RT(asuint) (ywant); 11231914882SAlex Richardson } 11331914882SAlex Richardson #endif 11431914882SAlex Richardson 115*f3087befSAndrew Turner static inline void T (call_fenv) (const struct fun *f, struct T (args) a, 116*f3087befSAndrew Turner int r, RT (float) * y, int *ex, 117*f3087befSAndrew Turner const struct conf *conf) 11831914882SAlex Richardson { 11931914882SAlex Richardson if (r != FE_TONEAREST) 12031914882SAlex Richardson fesetround (r); 12131914882SAlex Richardson feclearexcept (FE_ALL_EXCEPT); 122*f3087befSAndrew Turner *y = T (call) (f, a, conf); 12331914882SAlex Richardson *ex = fetestexcept (FE_ALL_EXCEPT); 12431914882SAlex Richardson if (r != FE_TONEAREST) 12531914882SAlex Richardson fesetround (FE_TONEAREST); 12631914882SAlex Richardson } 12731914882SAlex Richardson 12831914882SAlex Richardson static inline void T (call_nofenv) (const struct fun *f, struct T (args) a, 129*f3087befSAndrew Turner int r, RT (float) * y, int *ex, 130*f3087befSAndrew Turner const struct conf *conf) 13131914882SAlex Richardson { 1325a02ffc3SAndrew Turner if (r != FE_TONEAREST) 1335a02ffc3SAndrew Turner fesetround (r); 134*f3087befSAndrew Turner *y = T (call) (f, a, conf); 13531914882SAlex Richardson *ex = 0; 1365a02ffc3SAndrew Turner if (r != FE_TONEAREST) 1375a02ffc3SAndrew Turner fesetround (FE_TONEAREST); 13831914882SAlex Richardson } 13931914882SAlex Richardson 14031914882SAlex Richardson static inline int T (call_long_fenv) (const struct fun *f, struct T (args) a, 14131914882SAlex Richardson int r, struct RT (ret) * p, 14231914882SAlex Richardson RT (float) ygot, int exgot) 14331914882SAlex Richardson { 14431914882SAlex Richardson if (r != FE_TONEAREST) 14531914882SAlex Richardson fesetround (r); 14631914882SAlex Richardson feclearexcept (FE_ALL_EXCEPT); 14731914882SAlex Richardson volatile struct T(args) va = a; // TODO: barrier 14831914882SAlex Richardson a = va; 14931914882SAlex Richardson RT(double) yl = T(call_long) (f, a); 15031914882SAlex Richardson p->y = (RT(float)) yl; 15131914882SAlex Richardson volatile RT(float) vy = p->y; // TODO: barrier 15231914882SAlex Richardson (void) vy; 15331914882SAlex Richardson p->ex = fetestexcept (FE_ALL_EXCEPT); 15431914882SAlex Richardson if (r != FE_TONEAREST) 15531914882SAlex Richardson fesetround (FE_TONEAREST); 15631914882SAlex Richardson p->ex_may = FE_INEXACT; 15731914882SAlex Richardson if (RT(isok) (ygot, exgot, p->y, p->ex, p->ex_may)) 15831914882SAlex Richardson return 1; 15931914882SAlex Richardson p->ulpexp = RT(ulpscale) (p->y); 16031914882SAlex Richardson if (isinf (p->y)) 16131914882SAlex Richardson p->tail = RT(lscalbn) (yl - (RT(double)) 2 * RT(halfinf), -p->ulpexp); 16231914882SAlex Richardson else 16331914882SAlex Richardson p->tail = RT(lscalbn) (yl - p->y, -p->ulpexp); 16431914882SAlex Richardson if (RT(fabs) (p->y) < RT(min_normal)) 16531914882SAlex Richardson { 16631914882SAlex Richardson /* TODO: subnormal result is treated as undeflow even if it's 16731914882SAlex Richardson exact since call_long may not raise inexact correctly. */ 16831914882SAlex Richardson if (p->y != 0 || (p->ex & FE_INEXACT)) 16931914882SAlex Richardson p->ex |= FE_UNDERFLOW | FE_INEXACT; 17031914882SAlex Richardson } 17131914882SAlex Richardson return 0; 17231914882SAlex Richardson } 17331914882SAlex Richardson static inline int T(call_long_nofenv) (const struct fun *f, struct T(args) a, 17431914882SAlex Richardson int r, struct RT(ret) * p, 17531914882SAlex Richardson RT(float) ygot, int exgot) 17631914882SAlex Richardson { 1775a02ffc3SAndrew Turner if (r != FE_TONEAREST) 1785a02ffc3SAndrew Turner fesetround (r); 17931914882SAlex Richardson RT(double) yl = T(call_long) (f, a); 18031914882SAlex Richardson p->y = (RT(float)) yl; 1815a02ffc3SAndrew Turner if (r != FE_TONEAREST) 1825a02ffc3SAndrew Turner fesetround (FE_TONEAREST); 18331914882SAlex Richardson if (RT(isok_nofenv) (ygot, p->y)) 18431914882SAlex Richardson return 1; 18531914882SAlex Richardson p->ulpexp = RT(ulpscale) (p->y); 18631914882SAlex Richardson if (isinf (p->y)) 18731914882SAlex Richardson p->tail = RT(lscalbn) (yl - (RT(double)) 2 * RT(halfinf), -p->ulpexp); 18831914882SAlex Richardson else 18931914882SAlex Richardson p->tail = RT(lscalbn) (yl - p->y, -p->ulpexp); 19031914882SAlex Richardson return 0; 19131914882SAlex Richardson } 19231914882SAlex Richardson 19331914882SAlex Richardson /* There are nan input args and all quiet. */ 19431914882SAlex Richardson static inline int T(qnanpropagation) (struct T(args) a) 19531914882SAlex Richardson { 19631914882SAlex Richardson return T(reduce) (a, isnan, ||) && !T(reduce) (a, RT(issignaling), ||); 19731914882SAlex Richardson } 19831914882SAlex Richardson static inline RT(float) T(sum) (struct T(args) a) 19931914882SAlex Richardson { 20031914882SAlex Richardson return T(reduce) (a, , +); 20131914882SAlex Richardson } 20231914882SAlex Richardson 20331914882SAlex Richardson /* returns 1 if the got result is ok. */ 20431914882SAlex Richardson static inline int T(call_mpfr_fix) (const struct fun *f, struct T(args) a, 20531914882SAlex Richardson int r_fenv, struct RT(ret) * p, 20631914882SAlex Richardson RT(float) ygot, int exgot) 20731914882SAlex Richardson { 20831914882SAlex Richardson #if USE_MPFR 20931914882SAlex Richardson int t, t2; 21031914882SAlex Richardson mpfr_rnd_t r = rmap (r_fenv); 21131914882SAlex Richardson MPFR_DECL_INIT(my, RT(prec_mpfr)); 21231914882SAlex Richardson MPFR_DECL_INIT(mr, RT(prec)); 21331914882SAlex Richardson MPFR_DECL_INIT(me, RT(prec_mpfr)); 21431914882SAlex Richardson mpfr_clear_flags (); 21531914882SAlex Richardson t = T(call_mpfr) (my, f, a, r); 21631914882SAlex Richardson /* Double rounding. */ 21731914882SAlex Richardson t2 = mpfr_set (mr, my, r); 21831914882SAlex Richardson if (t2) 21931914882SAlex Richardson t = t2; 22031914882SAlex Richardson mpfr_set_emin (RT(emin)); 22131914882SAlex Richardson mpfr_set_emax (RT(emax)); 22231914882SAlex Richardson t = mpfr_check_range (mr, t, r); 22331914882SAlex Richardson t = mpfr_subnormalize (mr, t, r); 22431914882SAlex Richardson mpfr_set_emax (MPFR_EMAX_DEFAULT); 22531914882SAlex Richardson mpfr_set_emin (MPFR_EMIN_DEFAULT); 22631914882SAlex Richardson p->y = mpfr_get_d (mr, r); 22731914882SAlex Richardson p->ex = t ? FE_INEXACT : 0; 22831914882SAlex Richardson p->ex_may = FE_INEXACT; 22931914882SAlex Richardson if (mpfr_underflow_p () && (p->ex & FE_INEXACT)) 23031914882SAlex Richardson /* TODO: handle before and after rounding uflow cases. */ 23131914882SAlex Richardson p->ex |= FE_UNDERFLOW; 23231914882SAlex Richardson if (mpfr_overflow_p ()) 23331914882SAlex Richardson p->ex |= FE_OVERFLOW | FE_INEXACT; 23431914882SAlex Richardson if (mpfr_divby0_p ()) 23531914882SAlex Richardson p->ex |= FE_DIVBYZERO; 23631914882SAlex Richardson //if (mpfr_erangeflag_p ()) 23731914882SAlex Richardson // p->ex |= FE_INVALID; 23831914882SAlex Richardson if (!mpfr_nanflag_p () && RT(isok) (ygot, exgot, p->y, p->ex, p->ex_may)) 23931914882SAlex Richardson return 1; 24031914882SAlex Richardson if (mpfr_nanflag_p () && !T(qnanpropagation) (a)) 24131914882SAlex Richardson p->ex |= FE_INVALID; 24231914882SAlex Richardson p->ulpexp = RT(ulpscale_mpfr) (my, t); 24331914882SAlex Richardson if (!isfinite (p->y)) 24431914882SAlex Richardson { 24531914882SAlex Richardson p->tail = 0; 24631914882SAlex Richardson if (isnan (p->y)) 24731914882SAlex Richardson { 24831914882SAlex Richardson /* If an input was nan keep its sign. */ 24931914882SAlex Richardson p->y = T(sum) (a); 25031914882SAlex Richardson if (!isnan (p->y)) 25131914882SAlex Richardson p->y = (p->y - p->y) / (p->y - p->y); 25231914882SAlex Richardson return RT(isok) (ygot, exgot, p->y, p->ex, p->ex_may); 25331914882SAlex Richardson } 25431914882SAlex Richardson mpfr_set_si_2exp (mr, signbit (p->y) ? -1 : 1, 1024, MPFR_RNDN); 25531914882SAlex Richardson if (mpfr_cmpabs (my, mr) >= 0) 25631914882SAlex Richardson return RT(isok) (ygot, exgot, p->y, p->ex, p->ex_may); 25731914882SAlex Richardson } 25831914882SAlex Richardson mpfr_sub (me, my, mr, MPFR_RNDN); 25931914882SAlex Richardson mpfr_mul_2si (me, me, -p->ulpexp, MPFR_RNDN); 26031914882SAlex Richardson p->tail = mpfr_get_d (me, MPFR_RNDN); 26131914882SAlex Richardson return 0; 26231914882SAlex Richardson #else 26331914882SAlex Richardson abort (); 26431914882SAlex Richardson #endif 26531914882SAlex Richardson } 26631914882SAlex Richardson 26731914882SAlex Richardson static int T(cmp) (const struct fun *f, struct gen *gen, 26831914882SAlex Richardson const struct conf *conf) 26931914882SAlex Richardson { 27031914882SAlex Richardson double maxerr = 0; 27131914882SAlex Richardson uint64_t cnt = 0; 27231914882SAlex Richardson uint64_t cnt1 = 0; 27331914882SAlex Richardson uint64_t cnt2 = 0; 27431914882SAlex Richardson uint64_t cntfail = 0; 27531914882SAlex Richardson int r = conf->r; 27631914882SAlex Richardson int use_mpfr = conf->mpfr; 27731914882SAlex Richardson int fenv = conf->fenv; 278*f3087befSAndrew Turner 27931914882SAlex Richardson for (;;) 28031914882SAlex Richardson { 28131914882SAlex Richardson struct RT(ret) want; 28231914882SAlex Richardson struct T(args) a = T(next) (gen); 28331914882SAlex Richardson int exgot; 28431914882SAlex Richardson int exgot2; 28531914882SAlex Richardson RT(float) ygot; 28631914882SAlex Richardson RT(float) ygot2; 28731914882SAlex Richardson int fail = 0; 28831914882SAlex Richardson if (fenv) 289*f3087befSAndrew Turner T (call_fenv) (f, a, r, &ygot, &exgot, conf); 29031914882SAlex Richardson else 291*f3087befSAndrew Turner T (call_nofenv) (f, a, r, &ygot, &exgot, conf); 29231914882SAlex Richardson if (f->twice) { 29331914882SAlex Richardson secondcall = 1; 29431914882SAlex Richardson if (fenv) 295*f3087befSAndrew Turner T (call_fenv) (f, a, r, &ygot2, &exgot2, conf); 29631914882SAlex Richardson else 297*f3087befSAndrew Turner T (call_nofenv) (f, a, r, &ygot2, &exgot2, conf); 29831914882SAlex Richardson secondcall = 0; 29931914882SAlex Richardson if (RT(asuint) (ygot) != RT(asuint) (ygot2)) 30031914882SAlex Richardson { 30131914882SAlex Richardson fail = 1; 30231914882SAlex Richardson cntfail++; 30331914882SAlex Richardson T(printcall) (f, a); 30431914882SAlex Richardson printf (" got %a then %a for same input\n", ygot, ygot2); 30531914882SAlex Richardson } 30631914882SAlex Richardson } 30731914882SAlex Richardson cnt++; 30831914882SAlex Richardson int ok = use_mpfr 30931914882SAlex Richardson ? T(call_mpfr_fix) (f, a, r, &want, ygot, exgot) 31031914882SAlex Richardson : (fenv ? T(call_long_fenv) (f, a, r, &want, ygot, exgot) 31131914882SAlex Richardson : T(call_long_nofenv) (f, a, r, &want, ygot, exgot)); 31231914882SAlex Richardson if (!ok) 31331914882SAlex Richardson { 31431914882SAlex Richardson int print = 0; 3155a02ffc3SAndrew Turner double err = RT (ulperr) (ygot, &want, r, conf->ignore_zero_sign); 31631914882SAlex Richardson double abserr = fabs (err); 31731914882SAlex Richardson // TODO: count errors below accuracy limit. 31831914882SAlex Richardson if (abserr > 0) 31931914882SAlex Richardson cnt1++; 32031914882SAlex Richardson if (abserr > 1) 32131914882SAlex Richardson cnt2++; 32231914882SAlex Richardson if (abserr > conf->errlim) 32331914882SAlex Richardson { 32431914882SAlex Richardson print = 1; 32531914882SAlex Richardson if (!fail) 32631914882SAlex Richardson { 32731914882SAlex Richardson fail = 1; 32831914882SAlex Richardson cntfail++; 32931914882SAlex Richardson } 33031914882SAlex Richardson } 33131914882SAlex Richardson if (abserr > maxerr) 33231914882SAlex Richardson { 33331914882SAlex Richardson maxerr = abserr; 33431914882SAlex Richardson if (!conf->quiet && abserr > conf->softlim) 33531914882SAlex Richardson print = 1; 33631914882SAlex Richardson } 33731914882SAlex Richardson if (print) 33831914882SAlex Richardson { 33931914882SAlex Richardson T(printcall) (f, a); 34031914882SAlex Richardson // TODO: inf ulp handling 34131914882SAlex Richardson printf (" got %a want %a %+g ulp err %g\n", ygot, want.y, 34231914882SAlex Richardson want.tail, err); 34331914882SAlex Richardson } 34431914882SAlex Richardson int diff = fenv ? exgot ^ want.ex : 0; 34531914882SAlex Richardson if (fenv && (diff & ~want.ex_may)) 34631914882SAlex Richardson { 34731914882SAlex Richardson if (!fail) 34831914882SAlex Richardson { 34931914882SAlex Richardson fail = 1; 35031914882SAlex Richardson cntfail++; 35131914882SAlex Richardson } 35231914882SAlex Richardson T(printcall) (f, a); 35331914882SAlex Richardson printf (" is %a %+g ulp, got except 0x%0x", want.y, want.tail, 35431914882SAlex Richardson exgot); 35531914882SAlex Richardson if (diff & exgot) 35631914882SAlex Richardson printf (" wrongly set: 0x%x", diff & exgot); 35731914882SAlex Richardson if (diff & ~exgot) 35831914882SAlex Richardson printf (" wrongly clear: 0x%x", diff & ~exgot); 35931914882SAlex Richardson putchar ('\n'); 36031914882SAlex Richardson } 36131914882SAlex Richardson } 36231914882SAlex Richardson if (cnt >= conf->n) 36331914882SAlex Richardson break; 36431914882SAlex Richardson if (!conf->quiet && cnt % 0x100000 == 0) 36531914882SAlex Richardson printf ("progress: %6.3f%% cnt %llu cnt1 %llu cnt2 %llu cntfail %llu " 36631914882SAlex Richardson "maxerr %g\n", 36731914882SAlex Richardson 100.0 * cnt / conf->n, (unsigned long long) cnt, 36831914882SAlex Richardson (unsigned long long) cnt1, (unsigned long long) cnt2, 36931914882SAlex Richardson (unsigned long long) cntfail, maxerr); 37031914882SAlex Richardson } 37131914882SAlex Richardson double cc = cnt; 37231914882SAlex Richardson if (cntfail) 37331914882SAlex Richardson printf ("FAIL "); 37431914882SAlex Richardson else 37531914882SAlex Richardson printf ("PASS "); 37631914882SAlex Richardson T(printgen) (f, gen); 37731914882SAlex Richardson printf (" round %c errlim %g maxerr %g %s cnt %llu cnt1 %llu %g%% cnt2 %llu " 37831914882SAlex Richardson "%g%% cntfail %llu %g%%\n", 37931914882SAlex Richardson conf->rc, conf->errlim, 38031914882SAlex Richardson maxerr, conf->r == FE_TONEAREST ? "+0.5" : "+1.0", 38131914882SAlex Richardson (unsigned long long) cnt, 38231914882SAlex Richardson (unsigned long long) cnt1, 100.0 * cnt1 / cc, 38331914882SAlex Richardson (unsigned long long) cnt2, 100.0 * cnt2 / cc, 38431914882SAlex Richardson (unsigned long long) cntfail, 100.0 * cntfail / cc); 38531914882SAlex Richardson return !!cntfail; 38631914882SAlex Richardson } 387