2 * Generic functions for ULP error estimation.
4 * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 * See https://llvm.org/LICENSE.txt for license information.
6 * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
9 /* For each different math function type,
10 T(x) should add a different suffix to x.
11 RT(x) should add a return type specific suffix to x. */
17 static int RT(ulpscale_mpfr
) (mpfr_t x
, int t
)
19 /* TODO: pow of 2 cases. */
20 if (mpfr_regular_p (x
))
22 mpfr_exp_t e
= mpfr_get_exp (x
) - RT(prec
);
25 if (e
> RT(emax
) - RT(prec
))
26 e
= RT(emax
) - RT(prec
);
32 return RT(emax
) - RT(prec
);
38 /* Difference between exact result and closest real number that
39 gets rounded to got, i.e. error before rounding, for a correctly
40 rounded result the difference is 0. */
41 static double RT(ulperr
) (RT(float) got
, const struct RT(ret
) * p
, int r
)
43 RT(float) want
= p
->y
;
47 if (RT(asuint
) (got
) == RT(asuint
) (want
))
49 if (signbit (got
) != signbit (want
))
50 /* May have false positives with NaN. */
51 //return isnan(got) && isnan(want) ? 0 : INFINITY;
53 if (!isfinite (want
) || !isfinite (got
))
55 if (isnan (got
) != isnan (want
))
61 got
= RT(copysign
) (RT(halfinf
), got
);
66 want
= RT(copysign
) (RT(halfinf
), want
);
70 if (r
== FE_TONEAREST
)
72 // TODO: incorrect when got vs want cross a powof2 boundary
74 ? got - want - tail ulp - 0.5 ulp
75 : got - want - tail ulp + 0.5 ulp; */
77 e
= d
> 0 ? -p
->tail
- 0.5 : -p
->tail
+ 0.5;
81 if ((r
== FE_DOWNWARD
&& got
< want
) || (r
== FE_UPWARD
&& got
> want
)
82 || (r
== FE_TOWARDZERO
&& fabs (got
) < fabs (want
)))
83 got
= RT(nextafter
) (got
, want
);
87 return RT(scalbn
) (d
, -p
->ulpexp
) + e
;
90 static int RT(isok
) (RT(float) ygot
, int exgot
, RT(float) ywant
, int exwant
,
93 return RT(asuint
) (ygot
) == RT(asuint
) (ywant
)
94 && ((exgot
^ exwant
) & ~exmay
) == 0;
97 static int RT(isok_nofenv
) (RT(float) ygot
, RT(float) ywant
)
99 return RT(asuint
) (ygot
) == RT(asuint
) (ywant
);
103 static inline void T(call_fenv
) (const struct fun
*f
, struct T(args
) a
, int r
,
104 RT(float) * y
, int *ex
)
106 if (r
!= FE_TONEAREST
)
108 feclearexcept (FE_ALL_EXCEPT
);
110 *ex
= fetestexcept (FE_ALL_EXCEPT
);
111 if (r
!= FE_TONEAREST
)
112 fesetround (FE_TONEAREST
);
115 static inline void T(call_nofenv
) (const struct fun
*f
, struct T(args
) a
,
116 int r
, RT(float) * y
, int *ex
)
122 static inline int T(call_long_fenv
) (const struct fun
*f
, struct T(args
) a
,
123 int r
, struct RT(ret
) * p
,
124 RT(float) ygot
, int exgot
)
126 if (r
!= FE_TONEAREST
)
128 feclearexcept (FE_ALL_EXCEPT
);
129 volatile struct T(args
) va
= a
; // TODO: barrier
131 RT(double) yl
= T(call_long
) (f
, a
);
132 p
->y
= (RT(float)) yl
;
133 volatile RT(float) vy
= p
->y
; // TODO: barrier
135 p
->ex
= fetestexcept (FE_ALL_EXCEPT
);
136 if (r
!= FE_TONEAREST
)
137 fesetround (FE_TONEAREST
);
138 p
->ex_may
= FE_INEXACT
;
139 if (RT(isok
) (ygot
, exgot
, p
->y
, p
->ex
, p
->ex_may
))
141 p
->ulpexp
= RT(ulpscale
) (p
->y
);
143 p
->tail
= RT(lscalbn
) (yl
- (RT(double)) 2 * RT(halfinf
), -p
->ulpexp
);
145 p
->tail
= RT(lscalbn
) (yl
- p
->y
, -p
->ulpexp
);
146 if (RT(fabs
) (p
->y
) < RT(min_normal
))
148 /* TODO: subnormal result is treated as undeflow even if it's
149 exact since call_long may not raise inexact correctly. */
150 if (p
->y
!= 0 || (p
->ex
& FE_INEXACT
))
151 p
->ex
|= FE_UNDERFLOW
| FE_INEXACT
;
155 static inline int T(call_long_nofenv
) (const struct fun
*f
, struct T(args
) a
,
156 int r
, struct RT(ret
) * p
,
157 RT(float) ygot
, int exgot
)
159 RT(double) yl
= T(call_long
) (f
, a
);
160 p
->y
= (RT(float)) yl
;
161 if (RT(isok_nofenv
) (ygot
, p
->y
))
163 p
->ulpexp
= RT(ulpscale
) (p
->y
);
165 p
->tail
= RT(lscalbn
) (yl
- (RT(double)) 2 * RT(halfinf
), -p
->ulpexp
);
167 p
->tail
= RT(lscalbn
) (yl
- p
->y
, -p
->ulpexp
);
171 /* There are nan input args and all quiet. */
172 static inline int T(qnanpropagation
) (struct T(args
) a
)
174 return T(reduce
) (a
, isnan
, ||) && !T(reduce
) (a
, RT(issignaling
), ||);
176 static inline RT(float) T(sum
) (struct T(args
) a
)
178 return T(reduce
) (a
, , +);
181 /* returns 1 if the got result is ok. */
182 static inline int T(call_mpfr_fix
) (const struct fun
*f
, struct T(args
) a
,
183 int r_fenv
, struct RT(ret
) * p
,
184 RT(float) ygot
, int exgot
)
188 mpfr_rnd_t r
= rmap (r_fenv
);
189 MPFR_DECL_INIT(my
, RT(prec_mpfr
));
190 MPFR_DECL_INIT(mr
, RT(prec
));
191 MPFR_DECL_INIT(me
, RT(prec_mpfr
));
193 t
= T(call_mpfr
) (my
, f
, a
, r
);
194 /* Double rounding. */
195 t2
= mpfr_set (mr
, my
, r
);
198 mpfr_set_emin (RT(emin
));
199 mpfr_set_emax (RT(emax
));
200 t
= mpfr_check_range (mr
, t
, r
);
201 t
= mpfr_subnormalize (mr
, t
, r
);
202 mpfr_set_emax (MPFR_EMAX_DEFAULT
);
203 mpfr_set_emin (MPFR_EMIN_DEFAULT
);
204 p
->y
= mpfr_get_d (mr
, r
);
205 p
->ex
= t
? FE_INEXACT
: 0;
206 p
->ex_may
= FE_INEXACT
;
207 if (mpfr_underflow_p () && (p
->ex
& FE_INEXACT
))
208 /* TODO: handle before and after rounding uflow cases. */
209 p
->ex
|= FE_UNDERFLOW
;
210 if (mpfr_overflow_p ())
211 p
->ex
|= FE_OVERFLOW
| FE_INEXACT
;
212 if (mpfr_divby0_p ())
213 p
->ex
|= FE_DIVBYZERO
;
214 //if (mpfr_erangeflag_p ())
215 // p->ex |= FE_INVALID;
216 if (!mpfr_nanflag_p () && RT(isok
) (ygot
, exgot
, p
->y
, p
->ex
, p
->ex_may
))
218 if (mpfr_nanflag_p () && !T(qnanpropagation
) (a
))
220 p
->ulpexp
= RT(ulpscale_mpfr
) (my
, t
);
221 if (!isfinite (p
->y
))
226 /* If an input was nan keep its sign. */
229 p
->y
= (p
->y
- p
->y
) / (p
->y
- p
->y
);
230 return RT(isok
) (ygot
, exgot
, p
->y
, p
->ex
, p
->ex_may
);
232 mpfr_set_si_2exp (mr
, signbit (p
->y
) ? -1 : 1, 1024, MPFR_RNDN
);
233 if (mpfr_cmpabs (my
, mr
) >= 0)
234 return RT(isok
) (ygot
, exgot
, p
->y
, p
->ex
, p
->ex_may
);
236 mpfr_sub (me
, my
, mr
, MPFR_RNDN
);
237 mpfr_mul_2si (me
, me
, -p
->ulpexp
, MPFR_RNDN
);
238 p
->tail
= mpfr_get_d (me
, MPFR_RNDN
);
245 static int T(cmp
) (const struct fun
*f
, struct gen
*gen
,
246 const struct conf
*conf
)
252 uint64_t cntfail
= 0;
254 int use_mpfr
= conf
->mpfr
;
255 int fenv
= conf
->fenv
;
259 struct T(args
) a
= T(next
) (gen
);
266 T(call_fenv
) (f
, a
, r
, &ygot
, &exgot
);
268 T(call_nofenv
) (f
, a
, r
, &ygot
, &exgot
);
272 T(call_fenv
) (f
, a
, r
, &ygot2
, &exgot2
);
274 T(call_nofenv
) (f
, a
, r
, &ygot2
, &exgot2
);
276 if (RT(asuint
) (ygot
) != RT(asuint
) (ygot2
))
281 printf (" got %a then %a for same input\n", ygot
, ygot2
);
286 ? T(call_mpfr_fix
) (f
, a
, r
, &want
, ygot
, exgot
)
287 : (fenv
? T(call_long_fenv
) (f
, a
, r
, &want
, ygot
, exgot
)
288 : T(call_long_nofenv
) (f
, a
, r
, &want
, ygot
, exgot
));
292 double err
= RT(ulperr
) (ygot
, &want
, r
);
293 double abserr
= fabs (err
);
294 // TODO: count errors below accuracy limit.
299 if (abserr
> conf
->errlim
)
311 if (!conf
->quiet
&& abserr
> conf
->softlim
)
317 // TODO: inf ulp handling
318 printf (" got %a want %a %+g ulp err %g\n", ygot
, want
.y
,
321 int diff
= fenv
? exgot
^ want
.ex
: 0;
322 if (fenv
&& (diff
& ~want
.ex_may
))
330 printf (" is %a %+g ulp, got except 0x%0x", want
.y
, want
.tail
,
333 printf (" wrongly set: 0x%x", diff
& exgot
);
335 printf (" wrongly clear: 0x%x", diff
& ~exgot
);
341 if (!conf
->quiet
&& cnt
% 0x100000 == 0)
342 printf ("progress: %6.3f%% cnt %llu cnt1 %llu cnt2 %llu cntfail %llu "
344 100.0 * cnt
/ conf
->n
, (unsigned long long) cnt
,
345 (unsigned long long) cnt1
, (unsigned long long) cnt2
,
346 (unsigned long long) cntfail
, maxerr
);
353 T(printgen
) (f
, gen
);
354 printf (" round %c errlim %g maxerr %g %s cnt %llu cnt1 %llu %g%% cnt2 %llu "
355 "%g%% cntfail %llu %g%%\n",
356 conf
->rc
, conf
->errlim
,
357 maxerr
, conf
->r
== FE_TONEAREST
? "+0.5" : "+1.0",
358 (unsigned long long) cnt
,
359 (unsigned long long) cnt1
, 100.0 * cnt1
/ cc
,
360 (unsigned long long) cnt2
, 100.0 * cnt2
/ cc
,
361 (unsigned long long) cntfail
, 100.0 * cntfail
/ cc
);