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
27static inline uint64_t
28asuint64 (double f)
29{
30  union
31  {
32    double f;
33    uint64_t i;
34  } u = {f};
35  return u.i;
36}
37
38static inline double
39asdouble (uint64_t i)
40{
41  union
42  {
43    uint64_t i;
44    double f;
45  } u = {i};
46  return u.f;
47}
48
49static inline uint32_t
50asuint (float f)
51{
52  union
53  {
54    float f;
55    uint32_t i;
56  } u = {f};
57  return u.i;
58}
59
60static inline float
61asfloat (uint32_t i)
62{
63  union
64  {
65    uint32_t i;
66    float f;
67  } u = {i};
68  return u.f;
69}
70
71static uint64_t seed = 0x0123456789abcdef;
72static uint64_t
73rand64 (void)
74{
75  seed = 6364136223846793005ull * seed + 1;
76  return seed ^ (seed >> 32);
77}
78
79/* Uniform random in [0,n].  */
80static uint64_t
81randn (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
99struct 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
110struct args_f1
111{
112  float x;
113};
114
115struct args_f2
116{
117  float x;
118  float x2;
119};
120
121struct args_d1
122{
123  double x;
124};
125
126struct args_d2
127{
128  double x;
129  double x2;
130};
131
132/* result = y + tail*2^ulpexp.  */
133struct ret_f
134{
135  float y;
136  double tail;
137  int ulpexp;
138  int ex;
139  int ex_may;
140};
141
142struct ret_d
143{
144  double y;
145  double tail;
146  int ulpexp;
147  int ex;
148  int ex_may;
149};
150
151static inline uint64_t
152next1 (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
164static inline uint64_t
165next2 (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
173static struct args_f1
174next_f1 (void *g)
175{
176  return (struct args_f1){asfloat (next1 (g))};
177}
178
179static struct args_f2
180next_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
187static struct args_d1
188next_d1 (void *g)
189{
190  return (struct args_d1){asdouble (next1 (g))};
191}
192
193static struct args_d2
194next_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
201struct 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.  */
217static int secondcall;
218
219/* Wrappers for vector functions.  */
220#ifdef __vpcs
221typedef __f32x4_t v_float;
222typedef __f64x2_t v_double;
223/* First element of fv and dv may be changed by -c argument.  */
224static float fv[2] = {1.0f, -INFINITY};
225static double dv[2] = {1.0, -INFINITY};
226static inline v_float argf(float x) { return (v_float){x,x,x,fv[secondcall]}; }
227static inline v_double argd(double x) { return (v_double){x,dv[secondcall]}; }
228#if WANT_SVE_MATH
229#include <arm_sve.h>
230typedef __SVFloat32_t sv_float;
231typedef __SVFloat64_t sv_double;
232
233static 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}
241static 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}
249static 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}
255static 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
266struct 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
297static 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
351static inline int
352ulpscale_f (float x)
353{
354  int e = asuint (x) >> 23 & 0xff;
355  if (!e)
356    e++;
357  return e - 0x7f - 23;
358}
359static inline int
360ulpscale_d (double x)
361{
362  int e = asuint64 (x) >> 52 & 0x7ff;
363  if (!e)
364    e++;
365  return e - 0x3ff - 52;
366}
367static inline float
368call_f1 (const struct fun *f, struct args_f1 a)
369{
370  return f->fun.f1 (a.x);
371}
372static inline float
373call_f2 (const struct fun *f, struct args_f2 a)
374{
375  return f->fun.f2 (a.x, a.x2);
376}
377
378static inline double
379call_d1 (const struct fun *f, struct args_d1 a)
380{
381  return f->fun.d1 (a.x);
382}
383static inline double
384call_d2 (const struct fun *f, struct args_d2 a)
385{
386  return f->fun.d2 (a.x, a.x2);
387}
388static inline double
389call_long_f1 (const struct fun *f, struct args_f1 a)
390{
391  return f->fun_long.f1 (a.x);
392}
393static inline double
394call_long_f2 (const struct fun *f, struct args_f2 a)
395{
396  return f->fun_long.f2 (a.x, a.x2);
397}
398static inline long double
399call_long_d1 (const struct fun *f, struct args_d1 a)
400{
401  return f->fun_long.d1 (a.x);
402}
403static inline long double
404call_long_d2 (const struct fun *f, struct args_d2 a)
405{
406  return f->fun_long.d2 (a.x, a.x2);
407}
408static inline void
409printcall_f1 (const struct fun *f, struct args_f1 a)
410{
411  printf ("%s(%a)", f->name, a.x);
412}
413static inline void
414printcall_f2 (const struct fun *f, struct args_f2 a)
415{
416  printf ("%s(%a, %a)", f->name, a.x, a.x2);
417}
418static inline void
419printcall_d1 (const struct fun *f, struct args_d1 a)
420{
421  printf ("%s(%a)", f->name, a.x);
422}
423static inline void
424printcall_d2 (const struct fun *f, struct args_d2 a)
425{
426  printf ("%s(%a, %a)", f->name, a.x, a.x2);
427}
428static inline void
429printgen_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}
434static inline void
435printgen_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}
441static inline void
442printgen_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}
447static inline void
448printgen_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
463static inline int
464issignaling_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}
471static inline int
472issignaling_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
481static mpfr_rnd_t
482rmap (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
506static inline int
507call_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}
513static inline int
514call_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}
522static inline int
523call_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}
529static inline int
530call_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
584static void
585usage (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
605static int
606cmp (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
622static uint64_t
623getnum (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
654static void
655parsegen (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
710int
711main (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