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