xref: /freebsd/contrib/arm-optimized-routines/math/test/ulp.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 /*
2  * ULP error checking tool for math functions.
3  *
4  * Copyright (c) 2019-2023, Arm Limited.
5  * SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception
6  */
7 
8 #define _GNU_SOURCE
9 #include <ctype.h>
10 #include <fenv.h>
11 #include <float.h>
12 #include <math.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include "mathlib.h"
18 
19 /* Don't depend on mpfr by default.  */
20 #ifndef USE_MPFR
21 # define USE_MPFR 0
22 #endif
23 #if USE_MPFR
24 # include <mpfr.h>
25 #endif
26 
27 static inline uint64_t
28 asuint64 (double f)
29 {
30   union
31   {
32     double f;
33     uint64_t i;
34   } u = {f};
35   return u.i;
36 }
37 
38 static inline double
39 asdouble (uint64_t i)
40 {
41   union
42   {
43     uint64_t i;
44     double f;
45   } u = {i};
46   return u.f;
47 }
48 
49 static inline uint32_t
50 asuint (float f)
51 {
52   union
53   {
54     float f;
55     uint32_t i;
56   } u = {f};
57   return u.i;
58 }
59 
60 static inline float
61 asfloat (uint32_t i)
62 {
63   union
64   {
65     uint32_t i;
66     float f;
67   } u = {i};
68   return u.f;
69 }
70 
71 static uint64_t seed = 0x0123456789abcdef;
72 static uint64_t
73 rand64 (void)
74 {
75   seed = 6364136223846793005ull * seed + 1;
76   return seed ^ (seed >> 32);
77 }
78 
79 /* Uniform random in [0,n].  */
80 static uint64_t
81 randn (uint64_t n)
82 {
83   uint64_t r, m;
84 
85   if (n == 0)
86     return 0;
87   n++;
88   if (n == 0)
89     return rand64 ();
90   for (;;)
91     {
92       r = rand64 ();
93       m = r % n;
94       if (r - m <= -n)
95 	return m;
96     }
97 }
98 
99 struct gen
100 {
101   uint64_t start;
102   uint64_t len;
103   uint64_t start2;
104   uint64_t len2;
105   uint64_t off;
106   uint64_t step;
107   uint64_t cnt;
108 };
109 
110 struct args_f1
111 {
112   float x;
113 };
114 
115 struct args_f2
116 {
117   float x;
118   float x2;
119 };
120 
121 struct args_d1
122 {
123   double x;
124 };
125 
126 struct args_d2
127 {
128   double x;
129   double x2;
130 };
131 
132 /* result = y + tail*2^ulpexp.  */
133 struct ret_f
134 {
135   float y;
136   double tail;
137   int ulpexp;
138   int ex;
139   int ex_may;
140 };
141 
142 struct ret_d
143 {
144   double y;
145   double tail;
146   int ulpexp;
147   int ex;
148   int ex_may;
149 };
150 
151 static inline uint64_t
152 next1 (struct gen *g)
153 {
154   /* For single argument use randomized incremental steps,
155      that produce dense sampling without collisions and allow
156      testing all inputs in a range.  */
157   uint64_t r = g->start + g->off;
158   g->off += g->step + randn (g->step / 2);
159   if (g->off > g->len)
160     g->off -= g->len; /* hack.  */
161   return r;
162 }
163 
164 static inline uint64_t
165 next2 (uint64_t *x2, struct gen *g)
166 {
167   /* For two arguments use uniform random sampling.  */
168   uint64_t r = g->start + randn (g->len);
169   *x2 = g->start2 + randn (g->len2);
170   return r;
171 }
172 
173 static struct args_f1
174 next_f1 (void *g)
175 {
176   return (struct args_f1){asfloat (next1 (g))};
177 }
178 
179 static struct args_f2
180 next_f2 (void *g)
181 {
182   uint64_t x2;
183   uint64_t x = next2 (&x2, g);
184   return (struct args_f2){asfloat (x), asfloat (x2)};
185 }
186 
187 static struct args_d1
188 next_d1 (void *g)
189 {
190   return (struct args_d1){asdouble (next1 (g))};
191 }
192 
193 static struct args_d2
194 next_d2 (void *g)
195 {
196   uint64_t x2;
197   uint64_t x = next2 (&x2, g);
198   return (struct args_d2){asdouble (x), asdouble (x2)};
199 }
200 
201 struct conf
202 {
203   int r;
204   int rc;
205   int quiet;
206   int mpfr;
207   int fenv;
208   unsigned long long n;
209   double softlim;
210   double errlim;
211   int ignore_zero_sign;
212 };
213 
214 /* A bit of a hack: call vector functions twice with the same
215    input in lane 0 but a different value in other lanes: once
216    with an in-range value and then with a special case value.  */
217 static int secondcall;
218 
219 /* Wrappers for vector functions.  */
220 #ifdef __vpcs
221 typedef __f32x4_t v_float;
222 typedef __f64x2_t v_double;
223 /* First element of fv and dv may be changed by -c argument.  */
224 static float fv[2] = {1.0f, -INFINITY};
225 static double dv[2] = {1.0, -INFINITY};
226 static inline v_float argf(float x) { return (v_float){x,x,x,fv[secondcall]}; }
227 static inline v_double argd(double x) { return (v_double){x,dv[secondcall]}; }
228 #if WANT_SVE_MATH
229 #include <arm_sve.h>
230 typedef __SVFloat32_t sv_float;
231 typedef __SVFloat64_t sv_double;
232 
233 static inline sv_float svargf(float x)  {
234 	int n = svcntw();
235 	float base[n];
236 	for (int i=0; i<n; i++)
237 		base[i] = (float)x;
238 	base[n-1] = (float) fv[secondcall];
239 	return svld1(svptrue_b32(), base);
240 }
241 static inline sv_double svargd(double x) {
242 	int n = svcntd();
243 	double base[n];
244 	for (int i=0; i<n; i++)
245 		base[i] = x;
246 	base[n-1] = dv[secondcall];
247 	return svld1(svptrue_b64(), base);
248 }
249 static inline float svretf(sv_float vec)  {
250 	int n = svcntw();
251 	float res[n];
252 	svst1(svptrue_b32(), res, vec);
253 	return res[0];
254 }
255 static inline double svretd(sv_double vec) {
256 	int n = svcntd();
257 	double res[n];
258 	svst1(svptrue_b64(), res, vec);
259 	return res[0];
260 }
261 #endif
262 #endif
263 
264 #include "test/ulp_wrappers.h"
265 
266 struct fun
267 {
268   const char *name;
269   int arity;
270   int singleprec;
271   int twice;
272   union
273   {
274     float (*f1) (float);
275     float (*f2) (float, float);
276     double (*d1) (double);
277     double (*d2) (double, double);
278   } fun;
279   union
280   {
281     double (*f1) (double);
282     double (*f2) (double, double);
283     long double (*d1) (long double);
284     long double (*d2) (long double, long double);
285   } fun_long;
286 #if USE_MPFR
287   union
288   {
289     int (*f1) (mpfr_t, const mpfr_t, mpfr_rnd_t);
290     int (*f2) (mpfr_t, const mpfr_t, const mpfr_t, mpfr_rnd_t);
291     int (*d1) (mpfr_t, const mpfr_t, mpfr_rnd_t);
292     int (*d2) (mpfr_t, const mpfr_t, const mpfr_t, mpfr_rnd_t);
293   } fun_mpfr;
294 #endif
295 };
296 
297 static const struct fun fun[] = {
298 #if USE_MPFR
299 # define F(x, x_wrap, x_long, x_mpfr, a, s, t, twice) \
300   {#x, a, s, twice, {.t = x_wrap}, {.t = x_long}, {.t = x_mpfr}},
301 #else
302 # define F(x, x_wrap, x_long, x_mpfr, a, s, t, twice) \
303   {#x, a, s, twice, {.t = x_wrap}, {.t = x_long}},
304 #endif
305 #define F1(x) F (x##f, x##f, x, mpfr_##x, 1, 1, f1, 0)
306 #define F2(x) F (x##f, x##f, x, mpfr_##x, 2, 1, f2, 0)
307 #define D1(x) F (x, x, x##l, mpfr_##x, 1, 0, d1, 0)
308 #define D2(x) F (x, x, x##l, mpfr_##x, 2, 0, d2, 0)
309 /* Neon routines.  */
310 #define VF1(x) F (__v_##x##f, v_##x##f, x, mpfr_##x, 1, 1, f1, 0)
311 #define VF2(x) F (__v_##x##f, v_##x##f, x, mpfr_##x, 2, 1, f2, 0)
312 #define VD1(x) F (__v_##x, v_##x, x##l, mpfr_##x, 1, 0, d1, 0)
313 #define VD2(x) F (__v_##x, v_##x, x##l, mpfr_##x, 2, 0, d2, 0)
314 #define VNF1(x) F (__vn_##x##f, vn_##x##f, x, mpfr_##x, 1, 1, f1, 0)
315 #define VNF2(x) F (__vn_##x##f, vn_##x##f, x, mpfr_##x, 2, 1, f2, 0)
316 #define VND1(x) F (__vn_##x, vn_##x, x##l, mpfr_##x, 1, 0, d1, 0)
317 #define VND2(x) F (__vn_##x, vn_##x, x##l, mpfr_##x, 2, 0, d2, 0)
318 #define ZVF1(x) F (_ZGVnN4v_##x##f, Z_##x##f, x, mpfr_##x, 1, 1, f1, 0)
319 #define ZVF2(x) F (_ZGVnN4vv_##x##f, Z_##x##f, x, mpfr_##x, 2, 1, f2, 0)
320 #define ZVD1(x) F (_ZGVnN2v_##x, Z_##x, x##l, mpfr_##x, 1, 0, d1, 0)
321 #define ZVD2(x) F (_ZGVnN2vv_##x, Z_##x, x##l, mpfr_##x, 2, 0, d2, 0)
322 #define ZVNF1(x) VNF1 (x) ZVF1 (x)
323 #define ZVNF2(x) VNF2 (x) ZVF2 (x)
324 #define ZVND1(x) VND1 (x) ZVD1 (x)
325 #define ZVND2(x) VND2 (x) ZVD2 (x)
326 /* SVE routines.  */
327 #define SVF1(x) F (__sv_##x##f, sv_##x##f, x, mpfr_##x, 1, 1, f1, 0)
328 #define SVF2(x) F (__sv_##x##f, sv_##x##f, x, mpfr_##x, 2, 1, f2, 0)
329 #define SVD1(x) F (__sv_##x, sv_##x, x##l, mpfr_##x, 1, 0, d1, 0)
330 #define SVD2(x) F (__sv_##x, sv_##x, x##l, mpfr_##x, 2, 0, d2, 0)
331 #define ZSVF1(x) F (_ZGVsMxv_##x##f, Z_sv_##x##f, x, mpfr_##x, 1, 1, f1, 0)
332 #define ZSVF2(x) F (_ZGVsMxvv_##x##f, Z_sv_##x##f, x, mpfr_##x, 2, 1, f2, 0)
333 #define ZSVD1(x) F (_ZGVsMxv_##x, Z_sv_##x, x##l, mpfr_##x, 1, 0, d1, 0)
334 #define ZSVD2(x) F (_ZGVsMxvv_##x, Z_sv_##x, x##l, mpfr_##x, 2, 0, d2, 0)
335 
336 #include "test/ulp_funcs.h"
337 
338 #undef F
339 #undef F1
340 #undef F2
341 #undef D1
342 #undef D2
343 #undef SVF1
344 #undef SVF2
345 #undef SVD1
346 #undef SVD2
347  {0}};
348 
349 /* Boilerplate for generic calls.  */
350 
351 static inline int
352 ulpscale_f (float x)
353 {
354   int e = asuint (x) >> 23 & 0xff;
355   if (!e)
356     e++;
357   return e - 0x7f - 23;
358 }
359 static inline int
360 ulpscale_d (double x)
361 {
362   int e = asuint64 (x) >> 52 & 0x7ff;
363   if (!e)
364     e++;
365   return e - 0x3ff - 52;
366 }
367 static inline float
368 call_f1 (const struct fun *f, struct args_f1 a)
369 {
370   return f->fun.f1 (a.x);
371 }
372 static inline float
373 call_f2 (const struct fun *f, struct args_f2 a)
374 {
375   return f->fun.f2 (a.x, a.x2);
376 }
377 
378 static inline double
379 call_d1 (const struct fun *f, struct args_d1 a)
380 {
381   return f->fun.d1 (a.x);
382 }
383 static inline double
384 call_d2 (const struct fun *f, struct args_d2 a)
385 {
386   return f->fun.d2 (a.x, a.x2);
387 }
388 static inline double
389 call_long_f1 (const struct fun *f, struct args_f1 a)
390 {
391   return f->fun_long.f1 (a.x);
392 }
393 static inline double
394 call_long_f2 (const struct fun *f, struct args_f2 a)
395 {
396   return f->fun_long.f2 (a.x, a.x2);
397 }
398 static inline long double
399 call_long_d1 (const struct fun *f, struct args_d1 a)
400 {
401   return f->fun_long.d1 (a.x);
402 }
403 static inline long double
404 call_long_d2 (const struct fun *f, struct args_d2 a)
405 {
406   return f->fun_long.d2 (a.x, a.x2);
407 }
408 static inline void
409 printcall_f1 (const struct fun *f, struct args_f1 a)
410 {
411   printf ("%s(%a)", f->name, a.x);
412 }
413 static inline void
414 printcall_f2 (const struct fun *f, struct args_f2 a)
415 {
416   printf ("%s(%a, %a)", f->name, a.x, a.x2);
417 }
418 static inline void
419 printcall_d1 (const struct fun *f, struct args_d1 a)
420 {
421   printf ("%s(%a)", f->name, a.x);
422 }
423 static inline void
424 printcall_d2 (const struct fun *f, struct args_d2 a)
425 {
426   printf ("%s(%a, %a)", f->name, a.x, a.x2);
427 }
428 static inline void
429 printgen_f1 (const struct fun *f, struct gen *gen)
430 {
431   printf ("%s in [%a;%a]", f->name, asfloat (gen->start),
432 	  asfloat (gen->start + gen->len));
433 }
434 static inline void
435 printgen_f2 (const struct fun *f, struct gen *gen)
436 {
437   printf ("%s in [%a;%a] x [%a;%a]", f->name, asfloat (gen->start),
438 	  asfloat (gen->start + gen->len), asfloat (gen->start2),
439 	  asfloat (gen->start2 + gen->len2));
440 }
441 static inline void
442 printgen_d1 (const struct fun *f, struct gen *gen)
443 {
444   printf ("%s in [%a;%a]", f->name, asdouble (gen->start),
445 	  asdouble (gen->start + gen->len));
446 }
447 static inline void
448 printgen_d2 (const struct fun *f, struct gen *gen)
449 {
450   printf ("%s in [%a;%a] x [%a;%a]", f->name, asdouble (gen->start),
451 	  asdouble (gen->start + gen->len), asdouble (gen->start2),
452 	  asdouble (gen->start2 + gen->len2));
453 }
454 
455 #define reduce_f1(a, f, op) (f (a.x))
456 #define reduce_f2(a, f, op) (f (a.x) op f (a.x2))
457 #define reduce_d1(a, f, op) (f (a.x))
458 #define reduce_d2(a, f, op) (f (a.x) op f (a.x2))
459 
460 #ifndef IEEE_754_2008_SNAN
461 # define IEEE_754_2008_SNAN 1
462 #endif
463 static inline int
464 issignaling_f (float x)
465 {
466   uint32_t ix = asuint (x);
467   if (!IEEE_754_2008_SNAN)
468     return (ix & 0x7fc00000) == 0x7fc00000;
469   return 2 * (ix ^ 0x00400000) > 2u * 0x7fc00000;
470 }
471 static inline int
472 issignaling_d (double x)
473 {
474   uint64_t ix = asuint64 (x);
475   if (!IEEE_754_2008_SNAN)
476     return (ix & 0x7ff8000000000000) == 0x7ff8000000000000;
477   return 2 * (ix ^ 0x0008000000000000) > 2 * 0x7ff8000000000000ULL;
478 }
479 
480 #if USE_MPFR
481 static mpfr_rnd_t
482 rmap (int r)
483 {
484   switch (r)
485     {
486     case FE_TONEAREST:
487       return MPFR_RNDN;
488     case FE_TOWARDZERO:
489       return MPFR_RNDZ;
490     case FE_UPWARD:
491       return MPFR_RNDU;
492     case FE_DOWNWARD:
493       return MPFR_RNDD;
494     }
495   return -1;
496 }
497 
498 #define prec_mpfr_f 50
499 #define prec_mpfr_d 80
500 #define prec_f 24
501 #define prec_d 53
502 #define emin_f -148
503 #define emin_d -1073
504 #define emax_f 128
505 #define emax_d 1024
506 static inline int
507 call_mpfr_f1 (mpfr_t y, const struct fun *f, struct args_f1 a, mpfr_rnd_t r)
508 {
509   MPFR_DECL_INIT (x, prec_f);
510   mpfr_set_flt (x, a.x, MPFR_RNDN);
511   return f->fun_mpfr.f1 (y, x, r);
512 }
513 static inline int
514 call_mpfr_f2 (mpfr_t y, const struct fun *f, struct args_f2 a, mpfr_rnd_t r)
515 {
516   MPFR_DECL_INIT (x, prec_f);
517   MPFR_DECL_INIT (x2, prec_f);
518   mpfr_set_flt (x, a.x, MPFR_RNDN);
519   mpfr_set_flt (x2, a.x2, MPFR_RNDN);
520   return f->fun_mpfr.f2 (y, x, x2, r);
521 }
522 static inline int
523 call_mpfr_d1 (mpfr_t y, const struct fun *f, struct args_d1 a, mpfr_rnd_t r)
524 {
525   MPFR_DECL_INIT (x, prec_d);
526   mpfr_set_d (x, a.x, MPFR_RNDN);
527   return f->fun_mpfr.d1 (y, x, r);
528 }
529 static inline int
530 call_mpfr_d2 (mpfr_t y, const struct fun *f, struct args_d2 a, mpfr_rnd_t r)
531 {
532   MPFR_DECL_INIT (x, prec_d);
533   MPFR_DECL_INIT (x2, prec_d);
534   mpfr_set_d (x, a.x, MPFR_RNDN);
535   mpfr_set_d (x2, a.x2, MPFR_RNDN);
536   return f->fun_mpfr.d2 (y, x, x2, r);
537 }
538 #endif
539 
540 #define float_f float
541 #define double_f double
542 #define copysign_f copysignf
543 #define nextafter_f nextafterf
544 #define fabs_f fabsf
545 #define asuint_f asuint
546 #define asfloat_f asfloat
547 #define scalbn_f scalbnf
548 #define lscalbn_f scalbn
549 #define halfinf_f 0x1p127f
550 #define min_normal_f 0x1p-126f
551 
552 #define float_d double
553 #define double_d long double
554 #define copysign_d copysign
555 #define nextafter_d nextafter
556 #define fabs_d fabs
557 #define asuint_d asuint64
558 #define asfloat_d asdouble
559 #define scalbn_d scalbn
560 #define lscalbn_d scalbnl
561 #define halfinf_d 0x1p1023
562 #define min_normal_d 0x1p-1022
563 
564 #define NEW_RT
565 #define RT(x) x##_f
566 #define T(x) x##_f1
567 #include "ulp.h"
568 #undef T
569 #define T(x) x##_f2
570 #include "ulp.h"
571 #undef T
572 #undef RT
573 
574 #define NEW_RT
575 #define RT(x) x##_d
576 #define T(x) x##_d1
577 #include "ulp.h"
578 #undef T
579 #define T(x) x##_d2
580 #include "ulp.h"
581 #undef T
582 #undef RT
583 
584 static void
585 usage (void)
586 {
587   puts ("./ulp [-q] [-m] [-f] [-r {n|u|d|z}] [-l soft-ulplimit] [-e ulplimit] func "
588 	"lo [hi [x lo2 hi2] [count]]");
589   puts ("Compares func against a higher precision implementation in [lo; hi].");
590   puts ("-q: quiet.");
591   puts ("-m: use mpfr even if faster method is available.");
592   puts ("-f: disable fenv exceptions testing.");
593 #ifdef ___vpcs
594   puts ("-c: neutral 'control value' to test behaviour when one lane can affect another. \n"
595 	"    This should be different from tested input in other lanes, and non-special \n"
596 	"    (i.e. should not trigger fenv exceptions). Default is 1.");
597 #endif
598   puts ("-z: ignore sign of 0.");
599   puts ("Supported func:");
600   for (const struct fun *f = fun; f->name; f++)
601     printf ("\t%s\n", f->name);
602   exit (1);
603 }
604 
605 static int
606 cmp (const struct fun *f, struct gen *gen, const struct conf *conf)
607 {
608   int r = 1;
609   if (f->arity == 1 && f->singleprec)
610     r = cmp_f1 (f, gen, conf);
611   else if (f->arity == 2 && f->singleprec)
612     r = cmp_f2 (f, gen, conf);
613   else if (f->arity == 1 && !f->singleprec)
614     r = cmp_d1 (f, gen, conf);
615   else if (f->arity == 2 && !f->singleprec)
616     r = cmp_d2 (f, gen, conf);
617   else
618     usage ();
619   return r;
620 }
621 
622 static uint64_t
623 getnum (const char *s, int singleprec)
624 {
625   //	int i;
626   uint64_t sign = 0;
627   //	char buf[12];
628 
629   if (s[0] == '+')
630     s++;
631   else if (s[0] == '-')
632     {
633       sign = singleprec ? 1ULL << 31 : 1ULL << 63;
634       s++;
635     }
636   /* 0xXXXX is treated as bit representation, '-' flips the sign bit.  */
637   if (s[0] == '0' && tolower (s[1]) == 'x' && strchr (s, 'p') == 0)
638     return sign ^ strtoull (s, 0, 0);
639   //	/* SNaN, QNaN, NaN, Inf.  */
640   //	for (i=0; s[i] && i < sizeof buf; i++)
641   //		buf[i] = tolower(s[i]);
642   //	buf[i] = 0;
643   //	if (strcmp(buf, "snan") == 0)
644   //		return sign | (singleprec ? 0x7fa00000 : 0x7ff4000000000000);
645   //	if (strcmp(buf, "qnan") == 0 || strcmp(buf, "nan") == 0)
646   //		return sign | (singleprec ? 0x7fc00000 : 0x7ff8000000000000);
647   //	if (strcmp(buf, "inf") == 0 || strcmp(buf, "infinity") == 0)
648   //		return sign | (singleprec ? 0x7f800000 : 0x7ff0000000000000);
649   /* Otherwise assume it's a floating-point literal.  */
650   return sign
651 	 | (singleprec ? asuint (strtof (s, 0)) : asuint64 (strtod (s, 0)));
652 }
653 
654 static void
655 parsegen (struct gen *g, int argc, char *argv[], const struct fun *f)
656 {
657   int singleprec = f->singleprec;
658   int arity = f->arity;
659   uint64_t a, b, a2, b2, n;
660   if (argc < 1)
661     usage ();
662   b = a = getnum (argv[0], singleprec);
663   n = 0;
664   if (argc > 1 && strcmp (argv[1], "x") == 0)
665     {
666       argc -= 2;
667       argv += 2;
668     }
669   else if (argc > 1)
670     {
671       b = getnum (argv[1], singleprec);
672       if (argc > 2 && strcmp (argv[2], "x") == 0)
673 	{
674 	  argc -= 3;
675 	  argv += 3;
676 	}
677     }
678   b2 = a2 = getnum (argv[0], singleprec);
679   if (argc > 1)
680     b2 = getnum (argv[1], singleprec);
681   if (argc > 2)
682     n = strtoull (argv[2], 0, 0);
683   if (argc > 3)
684     usage ();
685   //printf("ab %lx %lx ab2 %lx %lx n %lu\n", a, b, a2, b2, n);
686   if (arity == 1)
687     {
688       g->start = a;
689       g->len = b - a;
690       if (n - 1 > b - a)
691 	n = b - a + 1;
692       g->off = 0;
693       g->step = n ? (g->len + 1) / n : 1;
694       g->start2 = g->len2 = 0;
695       g->cnt = n;
696     }
697   else if (arity == 2)
698     {
699       g->start = a;
700       g->len = b - a;
701       g->off = g->step = 0;
702       g->start2 = a2;
703       g->len2 = b2 - a2;
704       g->cnt = n;
705     }
706   else
707     usage ();
708 }
709 
710 int
711 main (int argc, char *argv[])
712 {
713   const struct fun *f;
714   struct gen gen;
715   struct conf conf;
716   conf.rc = 'n';
717   conf.quiet = 0;
718   conf.mpfr = 0;
719   conf.fenv = 1;
720   conf.softlim = 0;
721   conf.errlim = INFINITY;
722   conf.ignore_zero_sign = 0;
723   for (;;)
724     {
725       argc--;
726       argv++;
727       if (argc < 1)
728 	usage ();
729       if (argv[0][0] != '-')
730 	break;
731       switch (argv[0][1])
732 	{
733 	case 'e':
734 	  argc--;
735 	  argv++;
736 	  if (argc < 1)
737 	    usage ();
738 	  conf.errlim = strtod (argv[0], 0);
739 	  break;
740 	case 'f':
741 	  conf.fenv = 0;
742 	  break;
743 	case 'l':
744 	  argc--;
745 	  argv++;
746 	  if (argc < 1)
747 	    usage ();
748 	  conf.softlim = strtod (argv[0], 0);
749 	  break;
750 	case 'm':
751 	  conf.mpfr = 1;
752 	  break;
753 	case 'q':
754 	  conf.quiet = 1;
755 	  break;
756 	case 'r':
757 	  conf.rc = argv[0][2];
758 	  if (!conf.rc)
759 	    {
760 	      argc--;
761 	      argv++;
762 	      if (argc < 1 || argv[0][1] != '\0')
763 		usage ();
764 	      conf.rc = argv[0][0];
765 	    }
766 	  break;
767 	case 'z':
768 	  conf.ignore_zero_sign = 1;
769 	  break;
770 #ifdef __vpcs
771 	case 'c':
772 	  argc--;
773 	  argv++;
774 	  fv[0] = strtof(argv[0], 0);
775 	  dv[0] = strtod(argv[0], 0);
776 	  break;
777 #endif
778 	default:
779 	  usage ();
780 	}
781     }
782   switch (conf.rc)
783     {
784     case 'n':
785       conf.r = FE_TONEAREST;
786       break;
787     case 'u':
788       conf.r = FE_UPWARD;
789       break;
790     case 'd':
791       conf.r = FE_DOWNWARD;
792       break;
793     case 'z':
794       conf.r = FE_TOWARDZERO;
795       break;
796     default:
797       usage ();
798     }
799   for (f = fun; f->name; f++)
800     if (strcmp (argv[0], f->name) == 0)
801       break;
802   if (!f->name)
803     {
804 #ifndef __vpcs
805       /* Ignore vector math functions if vector math is not supported.  */
806       if (strncmp (argv[0], "_ZGVnN", 6) == 0)
807 	exit (0);
808 #endif
809 #if !WANT_SVE_MATH
810       if (strncmp (argv[0], "_ZGVsMxv", 8) == 0)
811 	exit (0);
812 #endif
813       printf ("math function %s not supported\n", argv[0]);
814       exit (1);
815     }
816   if (!f->singleprec && LDBL_MANT_DIG == DBL_MANT_DIG)
817     conf.mpfr = 1; /* Use mpfr if long double has no extra precision.  */
818   if (!USE_MPFR && conf.mpfr)
819     {
820       puts ("mpfr is not available.");
821       return 0;
822     }
823   argc--;
824   argv++;
825   parsegen (&gen, argc, argv, f);
826   conf.n = gen.cnt;
827   return cmp (f, &gen, &conf);
828 }
829