Path: blob/main/contrib/arm-optimized-routines/math/test/mathtest.c
48254 views
/*1* mathtest.c - test rig for mathlib2*3* Copyright (c) 1998-2024, Arm Limited.4* SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception5*/6/* clang-format off */78#define _GNU_SOURCE9#include <assert.h>10#include <stdio.h>11#include <stdlib.h>12#include <string.h>13#include <setjmp.h>14#include <ctype.h>15#include <math.h>16#include <errno.h>17#include <limits.h>18#include <fenv.h>19#include "mathlib.h"2021#ifndef math_errhandling22# define math_errhandling 023#endif2425#ifdef __cplusplus26#define EXTERN_C extern "C"27#else28#define EXTERN_C extern29#endif3031#ifndef TRUE32#define TRUE 133#endif34#ifndef FALSE35#define FALSE 036#endif3738#ifdef IMPORT_SYMBOL39#define STR2(x) #x40#define STR(x) STR2(x)41_Pragma(STR(import IMPORT_SYMBOL))42#endif4344int dmsd, dlsd;45int quiet = 0;46int doround = 0;47unsigned statusmask = FE_ALL_EXCEPT;4849#define EXTRABITS (12)50#define ULPUNIT (1<<EXTRABITS)5152typedef int (*test) (void);5354/*55struct to hold info about a function (which could actually be a macro)56*/57typedef struct {58enum {59t_func, t_macro60} type;61enum {62at_d, at_s, /* double or single precision float */63at_d2, at_s2, /* same, but taking two args */64at_di, at_si, /* double/single and an int */65at_dip, at_sip, /* double/single and an int ptr */66at_ddp, at_ssp, /* d/s and a d/s ptr */67at_dc, at_sc, /* double or single precision complex */68at_dc2, at_sc2 /* same, but taking two args */69} argtype;70enum {71rt_d, rt_s, rt_i, /* double, single, int */72rt_dc, rt_sc, /* double, single precision complex */73rt_d2, rt_s2 /* also use res2 */74} rettype;75union {76void* ptr;77double (*d_d_ptr)(double);78float (*s_s_ptr)(float);79int (*d_i_ptr)(double);80int (*s_i_ptr)(float);81double (*d2_d_ptr)(double, double);82float (*s2_s_ptr)(float, float);83double (*di_d_ptr)(double,int);84float (*si_s_ptr)(float,int);85double (*dip_d_ptr)(double,int*);86float (*sip_s_ptr)(float,int*);87double (*ddp_d_ptr)(double,double*);88float (*ssp_s_ptr)(float,float*);89} func;90enum {91m_none,92m_isfinite, m_isfinitef,93m_isgreater, m_isgreaterequal,94m_isgreaterequalf, m_isgreaterf,95m_isinf, m_isinff,96m_isless, m_islessequal,97m_islessequalf, m_islessf,98m_islessgreater, m_islessgreaterf,99m_isnan, m_isnanf,100m_isnormal, m_isnormalf,101m_isunordered, m_isunorderedf,102m_fpclassify, m_fpclassifyf,103m_signbit, m_signbitf,104/* not actually a macro, but makes things easier */105m_rred, m_rredf,106m_cadd, m_csub, m_cmul, m_cdiv,107m_caddf, m_csubf, m_cmulf, m_cdivf108} macro_name; /* only used if a macro/something that can't be done using func */109long long tolerance;110const char* name;111} test_func;112113/* used in qsort */114int compare_tfuncs(const void* a, const void* b) {115return strcmp(((test_func*)a)->name, ((test_func*)b)->name);116}117118int is_double_argtype(int argtype) {119switch(argtype) {120case at_d:121case at_d2:122case at_dc:123case at_dc2:124return 1;125default:126return 0;127}128}129130int is_single_argtype(int argtype) {131switch(argtype) {132case at_s:133case at_s2:134case at_sc:135case at_sc2:136return 1;137default:138return 0;139}140}141142int is_double_rettype(int rettype) {143switch(rettype) {144case rt_d:145case rt_dc:146case rt_d2:147return 1;148default:149return 0;150}151}152153int is_single_rettype(int rettype) {154switch(rettype) {155case rt_s:156case rt_sc:157case rt_s2:158return 1;159default:160return 0;161}162}163164int is_complex_argtype(int argtype) {165switch(argtype) {166case at_dc:167case at_sc:168case at_dc2:169case at_sc2:170return 1;171default:172return 0;173}174}175176int is_complex_rettype(int rettype) {177switch(rettype) {178case rt_dc:179case rt_sc:180return 1;181default:182return 0;183}184}185186/*187* Special-case flags indicating that some functions' error188* tolerance handling is more complicated than a fixed relative189* error bound.190*/191#define ABSLOWERBOUND 0x4000000000000000LL192#define PLUSMINUSPIO2 0x1000000000000000LL193194#define ARM_PREFIX(x) x195196#define TFUNC(arg,ret,name,tolerance) { t_func, arg, ret, (void*)&name, m_none, tolerance, #name }197#define TFUNCARM(arg,ret,name,tolerance) { t_func, arg, ret, (void*)& ARM_PREFIX(name), m_none, tolerance, #name }198#define MFUNC(arg,ret,name,tolerance) { t_macro, arg, ret, NULL, m_##name, tolerance, #name }199200/* sincosf wrappers for easier testing. */201static float sincosf_sinf(float x) { float s,c; sincosf(x, &s, &c); return s; }202static float sincosf_cosf(float x) { float s,c; sincosf(x, &s, &c); return c; }203204test_func tfuncs[] = {205/* trigonometric */206TFUNC(at_d,rt_d, acos, 4*ULPUNIT),207TFUNC(at_d,rt_d, asin, 4*ULPUNIT),208TFUNC(at_d,rt_d, atan, 4*ULPUNIT),209TFUNC(at_d2,rt_d, atan2, 4*ULPUNIT),210211TFUNC(at_d,rt_d, tan, 2*ULPUNIT),212TFUNC(at_d,rt_d, sin, 2*ULPUNIT),213TFUNC(at_d,rt_d, cos, 2*ULPUNIT),214215TFUNC(at_s,rt_s, acosf, 4*ULPUNIT),216TFUNC(at_s,rt_s, asinf, 4*ULPUNIT),217TFUNC(at_s,rt_s, atanf, 4*ULPUNIT),218TFUNC(at_s2,rt_s, atan2f, 4*ULPUNIT),219TFUNCARM(at_s,rt_s, tanf, 4*ULPUNIT),220TFUNCARM(at_s,rt_s, sinf, 3*ULPUNIT/4),221TFUNCARM(at_s,rt_s, cosf, 3*ULPUNIT/4),222TFUNCARM(at_s,rt_s, sincosf_sinf, 3*ULPUNIT/4),223TFUNCARM(at_s,rt_s, sincosf_cosf, 3*ULPUNIT/4),224225/* hyperbolic */226TFUNC(at_d, rt_d, atanh, 4*ULPUNIT),227TFUNC(at_d, rt_d, asinh, 4*ULPUNIT),228TFUNC(at_d, rt_d, acosh, 4*ULPUNIT),229TFUNC(at_d,rt_d, tanh, 4*ULPUNIT),230TFUNC(at_d,rt_d, sinh, 4*ULPUNIT),231TFUNC(at_d,rt_d, cosh, 4*ULPUNIT),232233TFUNC(at_s, rt_s, atanhf, 4*ULPUNIT),234TFUNC(at_s, rt_s, asinhf, 4*ULPUNIT),235TFUNC(at_s, rt_s, acoshf, 4*ULPUNIT),236TFUNC(at_s,rt_s, tanhf, 4*ULPUNIT),237TFUNC(at_s,rt_s, sinhf, 4*ULPUNIT),238TFUNC(at_s,rt_s, coshf, 4*ULPUNIT),239240/* exponential and logarithmic */241TFUNC(at_d,rt_d, log, 3*ULPUNIT/4),242TFUNC(at_d,rt_d, log10, 3*ULPUNIT),243TFUNC(at_d,rt_d, log2, 3*ULPUNIT/4),244TFUNC(at_d,rt_d, log1p, 2*ULPUNIT),245TFUNC(at_d,rt_d, exp, 3*ULPUNIT/4),246TFUNC(at_d,rt_d, exp2, 3*ULPUNIT/4),247TFUNC(at_d,rt_d, expm1, ULPUNIT),248TFUNCARM(at_s,rt_s, logf, ULPUNIT),249TFUNC(at_s,rt_s, log10f, 3*ULPUNIT),250TFUNCARM(at_s,rt_s, log2f, ULPUNIT),251TFUNC(at_s,rt_s, log1pf, 2*ULPUNIT),252TFUNCARM(at_s,rt_s, expf, 3*ULPUNIT/4),253TFUNCARM(at_s,rt_s, exp2f, 3*ULPUNIT/4),254TFUNC(at_s,rt_s, expm1f, ULPUNIT),255#if WANT_EXP10_TESTS256TFUNC(at_d,rt_d, exp10, ULPUNIT),257#endif258259/* power */260TFUNC(at_d2,rt_d, pow, 3*ULPUNIT/4),261TFUNC(at_d,rt_d, sqrt, ULPUNIT/2),262TFUNC(at_d,rt_d, cbrt, 2*ULPUNIT),263TFUNC(at_d2, rt_d, hypot, 4*ULPUNIT),264265TFUNCARM(at_s2,rt_s, powf, ULPUNIT),266TFUNC(at_s,rt_s, sqrtf, ULPUNIT/2),267TFUNC(at_s,rt_s, cbrtf, 2*ULPUNIT),268TFUNC(at_s2, rt_s, hypotf, 4*ULPUNIT),269270/* error function */271TFUNC(at_d,rt_d, erf, 16*ULPUNIT),272TFUNC(at_s,rt_s, erff, 16*ULPUNIT),273TFUNC(at_d,rt_d, erfc, 16*ULPUNIT),274TFUNC(at_s,rt_s, erfcf, 16*ULPUNIT),275276/* gamma functions */277TFUNC(at_d,rt_d, tgamma, 16*ULPUNIT),278TFUNC(at_s,rt_s, tgammaf, 16*ULPUNIT),279TFUNC(at_d,rt_d, lgamma, 16*ULPUNIT | ABSLOWERBOUND),280TFUNC(at_s,rt_s, lgammaf, 16*ULPUNIT | ABSLOWERBOUND),281282TFUNC(at_d,rt_d, ceil, 0),283TFUNC(at_s,rt_s, ceilf, 0),284TFUNC(at_d2,rt_d, copysign, 0),285TFUNC(at_s2,rt_s, copysignf, 0),286TFUNC(at_d,rt_d, floor, 0),287TFUNC(at_s,rt_s, floorf, 0),288TFUNC(at_d2,rt_d, fmax, 0),289TFUNC(at_s2,rt_s, fmaxf, 0),290TFUNC(at_d2,rt_d, fmin, 0),291TFUNC(at_s2,rt_s, fminf, 0),292TFUNC(at_d2,rt_d, fmod, 0),293TFUNC(at_s2,rt_s, fmodf, 0),294MFUNC(at_d, rt_i, fpclassify, 0),295MFUNC(at_s, rt_i, fpclassifyf, 0),296TFUNC(at_dip,rt_d, frexp, 0),297TFUNC(at_sip,rt_s, frexpf, 0),298MFUNC(at_d, rt_i, isfinite, 0),299MFUNC(at_s, rt_i, isfinitef, 0),300MFUNC(at_d, rt_i, isgreater, 0),301MFUNC(at_d, rt_i, isgreaterequal, 0),302MFUNC(at_s, rt_i, isgreaterequalf, 0),303MFUNC(at_s, rt_i, isgreaterf, 0),304MFUNC(at_d, rt_i, isinf, 0),305MFUNC(at_s, rt_i, isinff, 0),306MFUNC(at_d, rt_i, isless, 0),307MFUNC(at_d, rt_i, islessequal, 0),308MFUNC(at_s, rt_i, islessequalf, 0),309MFUNC(at_s, rt_i, islessf, 0),310MFUNC(at_d, rt_i, islessgreater, 0),311MFUNC(at_s, rt_i, islessgreaterf, 0),312MFUNC(at_d, rt_i, isnan, 0),313MFUNC(at_s, rt_i, isnanf, 0),314MFUNC(at_d, rt_i, isnormal, 0),315MFUNC(at_s, rt_i, isnormalf, 0),316MFUNC(at_d, rt_i, isunordered, 0),317MFUNC(at_s, rt_i, isunorderedf, 0),318TFUNC(at_di,rt_d, ldexp, 0),319TFUNC(at_si,rt_s, ldexpf, 0),320TFUNC(at_ddp,rt_d2, modf, 0),321TFUNC(at_ssp,rt_s2, modff, 0),322#ifndef BIGRANGERED323MFUNC(at_d, rt_d, rred, 2*ULPUNIT),324#else325MFUNC(at_d, rt_d, m_rred, ULPUNIT),326#endif327MFUNC(at_d, rt_i, signbit, 0),328MFUNC(at_s, rt_i, signbitf, 0),329};330331/*332* keywords are: func size op1 op2 result res2 errno op1r op1i op2r op2i resultr resulti333* also we ignore: wrongresult wrongres2 wrongerrno334* op1 equivalent to op1r, same with op2 and result335*/336337typedef struct {338test_func *func;339unsigned op1r[2]; /* real part, also used for non-complex numbers */340unsigned op1i[2]; /* imaginary part */341unsigned op2r[2];342unsigned op2i[2];343unsigned resultr[3];344unsigned resulti[3];345enum {346rc_none, rc_zero, rc_infinity, rc_nan, rc_finite347} resultc; /* special complex results, rc_none means use resultr and resulti as normal */348unsigned res2[2];349unsigned status; /* IEEE status return, if any */350unsigned maybestatus; /* for optional status, or allowance for spurious */351int nresult; /* number of result words */352int in_err, in_err_limit;353int err;354int maybeerr;355int valid;356int comment;357int random;358} testdetail;359360enum { /* keywords */361k_errno, k_errno_in, k_error, k_func, k_maybeerror, k_maybestatus, k_op1, k_op1i, k_op1r, k_op2, k_op2i, k_op2r,362k_random, k_res2, k_result, k_resultc, k_resulti, k_resultr, k_status,363k_wrongres2, k_wrongresult, k_wrongstatus, k_wrongerrno364};365char *keywords[] = {366"errno", "errno_in", "error", "func", "maybeerror", "maybestatus", "op1", "op1i", "op1r", "op2", "op2i", "op2r",367"random", "res2", "result", "resultc", "resulti", "resultr", "status",368"wrongres2", "wrongresult", "wrongstatus", "wrongerrno"369};370371enum {372e_0, e_EDOM, e_ERANGE,373374/*375* This enum makes sure that we have the right number of errnos in the376* errno[] array377*/378e_number_of_errnos379};380char *errnos[] = {381"0", "EDOM", "ERANGE"382};383384enum {385e_none, e_divbyzero, e_domain, e_overflow, e_underflow386};387char *errors[] = {388"0", "divbyzero", "domain", "overflow", "underflow"389};390391static int verbose, fo, strict;392393/* state toggled by random=on / random=off */394static int randomstate;395396/* Canonify a double NaN: SNaNs all become 7FF00000.00000001 and QNaNs397* all become 7FF80000.00000001 */398void canon_dNaN(unsigned a[2]) {399if ((a[0] & 0x7FF00000) != 0x7FF00000)400return; /* not Inf or NaN */401if (!(a[0] & 0xFFFFF) && !a[1])402return; /* Inf */403a[0] &= 0x7FF80000; /* canonify top word */404a[1] = 0x00000001; /* canonify bottom word */405}406407/* Canonify a single NaN: SNaNs all become 7F800001 and QNaNs408* all become 7FC00001. Returns classification of the NaN. */409void canon_sNaN(unsigned a[1]) {410if ((a[0] & 0x7F800000) != 0x7F800000)411return; /* not Inf or NaN */412if (!(a[0] & 0x7FFFFF))413return; /* Inf */414a[0] &= 0x7FC00000; /* canonify most bits */415a[0] |= 0x00000001; /* canonify bottom bit */416}417418/*419* Detect difficult operands for FO mode.420*/421int is_dhard(unsigned a[2])422{423if ((a[0] & 0x7FF00000) == 0x7FF00000)424return TRUE; /* inf or NaN */425if ((a[0] & 0x7FF00000) == 0 &&426((a[0] & 0x7FFFFFFF) | a[1]) != 0)427return TRUE; /* denormal */428return FALSE;429}430int is_shard(unsigned a[1])431{432if ((a[0] & 0x7F800000) == 0x7F800000)433return TRUE; /* inf or NaN */434if ((a[0] & 0x7F800000) == 0 &&435(a[0] & 0x7FFFFFFF) != 0)436return TRUE; /* denormal */437return FALSE;438}439440/*441* Normalise all zeroes into +0, for FO mode.442*/443void dnormzero(unsigned a[2])444{445if (a[0] == 0x80000000 && a[1] == 0)446a[0] = 0;447}448void snormzero(unsigned a[1])449{450if (a[0] == 0x80000000)451a[0] = 0;452}453454static int find(char *word, char **array, int asize) {455int i, j;456457asize /= sizeof(char *);458459i = -1; j = asize; /* strictly between i and j */460while (j-i > 1) {461int k = (i+j) / 2;462int c = strcmp(word, array[k]);463if (c > 0)464i = k;465else if (c < 0)466j = k;467else /* found it! */468return k;469}470return -1; /* not found */471}472473static test_func* find_testfunc(char *word) {474int i, j, asize;475476asize = sizeof(tfuncs)/sizeof(test_func);477478i = -1; j = asize; /* strictly between i and j */479while (j-i > 1) {480int k = (i+j) / 2;481int c = strcmp(word, tfuncs[k].name);482if (c > 0)483i = k;484else if (c < 0)485j = k;486else /* found it! */487return tfuncs + k;488}489return NULL; /* not found */490}491492static long long calc_error(unsigned a[2], unsigned b[3], int shift, int rettype) {493unsigned r0, r1, r2;494int sign, carry;495long long result;496497/*498* If either number is infinite, require exact equality. If499* either number is NaN, require that both are NaN. If either500* of these requirements is broken, return INT_MAX.501*/502if (is_double_rettype(rettype)) {503if ((a[0] & 0x7FF00000) == 0x7FF00000 ||504(b[0] & 0x7FF00000) == 0x7FF00000) {505if (((a[0] & 0x800FFFFF) || a[1]) &&506((b[0] & 0x800FFFFF) || b[1]) &&507(a[0] & 0x7FF00000) == 0x7FF00000 &&508(b[0] & 0x7FF00000) == 0x7FF00000)509return 0; /* both NaN - OK */510if (!((a[0] & 0xFFFFF) || a[1]) &&511!((b[0] & 0xFFFFF) || b[1]) &&512a[0] == b[0])513return 0; /* both same sign of Inf - OK */514return LLONG_MAX;515}516} else {517if ((a[0] & 0x7F800000) == 0x7F800000 ||518(b[0] & 0x7F800000) == 0x7F800000) {519if ((a[0] & 0x807FFFFF) &&520(b[0] & 0x807FFFFF) &&521(a[0] & 0x7F800000) == 0x7F800000 &&522(b[0] & 0x7F800000) == 0x7F800000)523return 0; /* both NaN - OK */524if (!(a[0] & 0x7FFFFF) &&525!(b[0] & 0x7FFFFF) &&526a[0] == b[0])527return 0; /* both same sign of Inf - OK */528return LLONG_MAX;529}530}531532/*533* Both finite. Return INT_MAX if the signs differ.534*/535if ((a[0] ^ b[0]) & 0x80000000)536return LLONG_MAX;537538/*539* Now it's just straight multiple-word subtraction.540*/541if (is_double_rettype(rettype)) {542r2 = -b[2]; carry = (r2 == 0);543r1 = a[1] + ~b[1] + carry; carry = (r1 < a[1] || (carry && r1 == a[1]));544r0 = a[0] + ~b[0] + carry;545} else {546r2 = -b[1]; carry = (r2 == 0);547r1 = a[0] + ~b[0] + carry; carry = (r1 < a[0] || (carry && r1 == a[0]));548r0 = ~0 + carry;549}550551/*552* Forgive larger errors in specialised cases.553*/554if (shift > 0) {555if (shift > 32*3)556return 0; /* all errors are forgiven! */557while (shift >= 32) {558r2 = r1;559r1 = r0;560r0 = -(r0 >> 31);561shift -= 32;562}563564if (shift > 0) {565r2 = (r2 >> shift) | (r1 << (32-shift));566r1 = (r1 >> shift) | (r0 << (32-shift));567r0 = (r0 >> shift) | ((-(r0 >> 31)) << (32-shift));568}569}570571if (r0 & 0x80000000) {572sign = 1;573r2 = ~r2; carry = (r2 == 0);574r1 = 0 + ~r1 + carry; carry = (carry && (r2 == 0));575r0 = 0 + ~r0 + carry;576} else {577sign = 0;578}579580if (r0 >= (1LL<<(31-EXTRABITS)))581return LLONG_MAX; /* many ulps out */582583result = (r2 >> (32-EXTRABITS)) & (ULPUNIT-1);584result |= r1 << EXTRABITS;585result |= (long long)r0 << (32+EXTRABITS);586if (sign)587result = -result;588return result;589}590591/* special named operands */592593typedef struct {594unsigned op1, op2;595char* name;596} special_op;597598static special_op special_ops_double[] = {599{0x00000000,0x00000000,"0"},600{0x3FF00000,0x00000000,"1"},601{0x7FF00000,0x00000000,"inf"},602{0x7FF80000,0x00000001,"qnan"},603{0x7FF00000,0x00000001,"snan"},604{0x3ff921fb,0x54442d18,"pi2"},605{0x400921fb,0x54442d18,"pi"},606{0x3fe921fb,0x54442d18,"pi4"},607{0x4002d97c,0x7f3321d2,"3pi4"},608};609610static special_op special_ops_float[] = {611{0x00000000,0,"0"},612{0x3f800000,0,"1"},613{0x7f800000,0,"inf"},614{0x7fc00000,0,"qnan"},615{0x7f800001,0,"snan"},616{0x3fc90fdb,0,"pi2"},617{0x40490fdb,0,"pi"},618{0x3f490fdb,0,"pi4"},619{0x4016cbe4,0,"3pi4"},620};621622/*623This is what is returned by the below functions.624We need it to handle the sign of the number625*/626static special_op tmp_op = {0,0,0};627628special_op* find_special_op_from_op(unsigned op1, unsigned op2, int is_double) {629int i;630special_op* sop;631if(is_double) {632sop = special_ops_double;633} else {634sop = special_ops_float;635}636for(i = 0; i < sizeof(special_ops_double)/sizeof(special_op); i++) {637if(sop->op1 == (op1&0x7fffffff) && sop->op2 == op2) {638if(tmp_op.name) free(tmp_op.name);639tmp_op.name = malloc(strlen(sop->name)+2);640if(op1>>31) {641sprintf(tmp_op.name,"-%s",sop->name);642} else {643strcpy(tmp_op.name,sop->name);644}645return &tmp_op;646}647sop++;648}649return NULL;650}651652special_op* find_special_op_from_name(const char* name, int is_double) {653int i, neg=0;654special_op* sop;655if(is_double) {656sop = special_ops_double;657} else {658sop = special_ops_float;659}660if(*name=='-') {661neg=1;662name++;663} else if(*name=='+') {664name++;665}666for(i = 0; i < sizeof(special_ops_double)/sizeof(special_op); i++) {667if(0 == strcmp(name,sop->name)) {668tmp_op.op1 = sop->op1;669if(neg) {670tmp_op.op1 |= 0x80000000;671}672tmp_op.op2 = sop->op2;673return &tmp_op;674}675sop++;676}677return NULL;678}679680/*681helper function for the below682type=0 for single, 1 for double, 2 for no sop683*/684int do_op(char* q, unsigned* op, const char* name, int num, int sop_type) {685int i;686int n=num;687special_op* sop = NULL;688for(i = 0; i < num; i++) {689op[i] = 0;690}691if(sop_type<2) {692sop = find_special_op_from_name(q,sop_type);693}694if(sop != NULL) {695op[0] = sop->op1;696op[1] = sop->op2;697} else {698switch(num) {699case 1: n = sscanf(q, "%x", &op[0]); break;700case 2: n = sscanf(q, "%x.%x", &op[0], &op[1]); break;701case 3: n = sscanf(q, "%x.%x.%x", &op[0], &op[1], &op[2]); break;702default: return -1;703}704}705if (verbose) {706printf("%s=",name);707for (i = 0; (i < n); ++i) printf("%x.", op[i]);708printf(" (n=%d)\n", n);709}710return n;711}712713testdetail parsetest(char *testbuf, testdetail oldtest) {714char *p; /* Current part of line: Option name */715char *q; /* Current part of line: Option value */716testdetail ret; /* What we return */717int k; /* Function enum from k_* */718int n; /* Used as returns for scanfs */719int argtype=2, rettype=2; /* for do_op */720721/* clear ret */722memset(&ret, 0, sizeof(ret));723724if (verbose) printf("Parsing line: %s\n", testbuf);725while (*testbuf && isspace(*testbuf)) testbuf++;726if (testbuf[0] == ';' || testbuf[0] == '#' || testbuf[0] == '!' ||727testbuf[0] == '>' || testbuf[0] == '\0') {728ret.comment = 1;729if (verbose) printf("Line is a comment\n");730return ret;731}732ret.comment = 0;733734if (*testbuf == '+') {735if (oldtest.valid) {736ret = oldtest; /* structure copy */737} else {738fprintf(stderr, "copy from invalid: ignored\n");739}740testbuf++;741}742743ret.random = randomstate;744745ret.in_err = 0;746ret.in_err_limit = e_number_of_errnos;747748p = strtok(testbuf, " \t");749while (p != NULL) {750q = strchr(p, '=');751if (!q)752goto balderdash;753*q++ = '\0';754k = find(p, keywords, sizeof(keywords));755switch (k) {756case k_random:757randomstate = (!strcmp(q, "on"));758ret.comment = 1;759return ret; /* otherwise ignore this line */760case k_func:761if (verbose) printf("func=%s ", q);762//ret.func = find(q, funcs, sizeof(funcs));763ret.func = find_testfunc(q);764if (ret.func == NULL)765{766if (verbose) printf("(id=unknown)\n");767goto balderdash;768}769if(is_single_argtype(ret.func->argtype))770argtype = 0;771else if(is_double_argtype(ret.func->argtype))772argtype = 1;773if(is_single_rettype(ret.func->rettype))774rettype = 0;775else if(is_double_rettype(ret.func->rettype))776rettype = 1;777//ret.size = sizes[ret.func];778if (verbose) printf("(name=%s) (size=%d)\n", ret.func->name, ret.func->argtype);779break;780case k_op1:781case k_op1r:782n = do_op(q,ret.op1r,"op1r",2,argtype);783if (n < 1)784goto balderdash;785break;786case k_op1i:787n = do_op(q,ret.op1i,"op1i",2,argtype);788if (n < 1)789goto balderdash;790break;791case k_op2:792case k_op2r:793n = do_op(q,ret.op2r,"op2r",2,argtype);794if (n < 1)795goto balderdash;796break;797case k_op2i:798n = do_op(q,ret.op2i,"op2i",2,argtype);799if (n < 1)800goto balderdash;801break;802case k_resultc:803puts(q);804if(strncmp(q,"inf",3)==0) {805ret.resultc = rc_infinity;806} else if(strcmp(q,"zero")==0) {807ret.resultc = rc_zero;808} else if(strcmp(q,"nan")==0) {809ret.resultc = rc_nan;810} else if(strcmp(q,"finite")==0) {811ret.resultc = rc_finite;812} else {813goto balderdash;814}815break;816case k_result:817case k_resultr:818n = (do_op)(q,ret.resultr,"resultr",3,rettype);819if (n < 1)820goto balderdash;821ret.nresult = n; /* assume real and imaginary have same no. words */822break;823case k_resulti:824n = do_op(q,ret.resulti,"resulti",3,rettype);825if (n < 1)826goto balderdash;827break;828case k_res2:829n = do_op(q,ret.res2,"res2",2,rettype);830if (n < 1)831goto balderdash;832break;833case k_status:834while (*q) {835if (*q == 'i') ret.status |= FE_INVALID;836if (*q == 'z') ret.status |= FE_DIVBYZERO;837if (*q == 'o') ret.status |= FE_OVERFLOW;838if (*q == 'u') ret.status |= FE_UNDERFLOW;839q++;840}841break;842case k_maybeerror:843n = find(q, errors, sizeof(errors));844if (n < 0)845goto balderdash;846if(math_errhandling&MATH_ERREXCEPT) {847switch(n) {848case e_domain: ret.maybestatus |= FE_INVALID; break;849case e_divbyzero: ret.maybestatus |= FE_DIVBYZERO; break;850case e_overflow: ret.maybestatus |= FE_OVERFLOW; break;851case e_underflow: ret.maybestatus |= FE_UNDERFLOW; break;852}853}854{855switch(n) {856case e_domain:857ret.maybeerr = e_EDOM; break;858case e_divbyzero:859case e_overflow:860case e_underflow:861ret.maybeerr = e_ERANGE; break;862}863}864case k_maybestatus:865while (*q) {866if (*q == 'i') ret.maybestatus |= FE_INVALID;867if (*q == 'z') ret.maybestatus |= FE_DIVBYZERO;868if (*q == 'o') ret.maybestatus |= FE_OVERFLOW;869if (*q == 'u') ret.maybestatus |= FE_UNDERFLOW;870q++;871}872break;873case k_error:874n = find(q, errors, sizeof(errors));875if (n < 0)876goto balderdash;877if(math_errhandling&MATH_ERREXCEPT) {878switch(n) {879case e_domain: ret.status |= FE_INVALID; break;880case e_divbyzero: ret.status |= FE_DIVBYZERO; break;881case e_overflow: ret.status |= FE_OVERFLOW; break;882case e_underflow: ret.status |= FE_UNDERFLOW; break;883}884}885if(math_errhandling&MATH_ERRNO) {886switch(n) {887case e_domain:888ret.err = e_EDOM; break;889case e_divbyzero:890case e_overflow:891case e_underflow:892ret.err = e_ERANGE; break;893}894}895if(!(math_errhandling&MATH_ERRNO)) {896switch(n) {897case e_domain:898ret.maybeerr = e_EDOM; break;899case e_divbyzero:900case e_overflow:901case e_underflow:902ret.maybeerr = e_ERANGE; break;903}904}905break;906case k_errno:907ret.err = find(q, errnos, sizeof(errnos));908if (ret.err < 0)909goto balderdash;910break;911case k_errno_in:912ret.in_err = find(q, errnos, sizeof(errnos));913if (ret.err < 0)914goto balderdash;915ret.in_err_limit = ret.in_err + 1;916break;917case k_wrongresult:918case k_wrongstatus:919case k_wrongres2:920case k_wrongerrno:921/* quietly ignore these keys */922break;923default:924goto balderdash;925}926p = strtok(NULL, " \t");927}928ret.valid = 1;929return ret;930931/* come here from almost any error */932balderdash:933ret.valid = 0;934return ret;935}936937typedef enum {938test_comment, /* deliberately not a test */939test_invalid, /* accidentally not a test */940test_decline, /* was a test, and wasn't run */941test_fail, /* was a test, and failed */942test_pass /* was a test, and passed */943} testresult;944945char failtext[512];946947typedef union {948unsigned i[2];949double f;950double da[2];951} dbl;952953typedef union {954unsigned i;955float f;956float da[2];957} sgl;958959/* helper function for runtest */960void print_error(int rettype, unsigned *result, char* text, char** failp) {961special_op *sop;962char *str;963964if(result) {965*failp += sprintf(*failp," %s=",text);966sop = find_special_op_from_op(result[0],result[1],is_double_rettype(rettype));967if(sop) {968*failp += sprintf(*failp,"%s",sop->name);969} else {970if(is_double_rettype(rettype)) {971str="%08x.%08x";972} else {973str="%08x";974}975*failp += sprintf(*failp,str,result[0],result[1]);976}977}978}979980981void print_ulps_helper(const char *name, long long ulps, char** failp) {982if(ulps == LLONG_MAX) {983*failp += sprintf(*failp, " %s=HUGE", name);984} else {985*failp += sprintf(*failp, " %s=%.3f", name, (double)ulps / ULPUNIT);986}987}988989/* for complex args make ulpsr or ulpsri = 0 to not print */990void print_ulps(int rettype, long long ulpsr, long long ulpsi, char** failp) {991if(is_complex_rettype(rettype)) {992if (ulpsr) print_ulps_helper("ulpsr",ulpsr,failp);993if (ulpsi) print_ulps_helper("ulpsi",ulpsi,failp);994} else {995if (ulpsr) print_ulps_helper("ulps",ulpsr,failp);996}997}998999int runtest(testdetail t) {1000int err, status;10011002dbl d_arg1, d_arg2, d_res, d_res2;1003sgl s_arg1, s_arg2, s_res, s_res2;10041005int deferred_decline = FALSE;1006char *failp = failtext;10071008unsigned int intres=0;10091010int res2_adjust = 0;10111012if (t.comment)1013return test_comment;1014if (!t.valid)1015return test_invalid;10161017/* Set IEEE status to mathlib-normal */1018feclearexcept(FE_ALL_EXCEPT);10191020/* Deal with operands */1021#define DO_DOP(arg,op) arg.i[dmsd] = t.op[0]; arg.i[dlsd] = t.op[1]1022DO_DOP(d_arg1,op1r);1023DO_DOP(d_arg2,op2r);1024s_arg1.i = t.op1r[0]; s_arg2.i = t.op2r[0];1025s_res.i = 0;10261027/*1028* Detect NaNs, infinities and denormals on input, and set a1029* deferred decline flag if we're in FO mode.1030*1031* (We defer the decline rather than doing it immediately1032* because even in FO mode the operation is not permitted to1033* crash or tight-loop; so we _run_ the test, and then ignore1034* all the results.)1035*/1036if (fo) {1037if (is_double_argtype(t.func->argtype) && is_dhard(t.op1r))1038deferred_decline = TRUE;1039if (t.func->argtype==at_d2 && is_dhard(t.op2r))1040deferred_decline = TRUE;1041if (is_single_argtype(t.func->argtype) && is_shard(t.op1r))1042deferred_decline = TRUE;1043if (t.func->argtype==at_s2 && is_shard(t.op2r))1044deferred_decline = TRUE;1045if (is_double_rettype(t.func->rettype) && is_dhard(t.resultr))1046deferred_decline = TRUE;1047if (t.func->rettype==rt_d2 && is_dhard(t.res2))1048deferred_decline = TRUE;1049if (is_single_argtype(t.func->rettype) && is_shard(t.resultr))1050deferred_decline = TRUE;1051if (t.func->rettype==rt_s2 && is_shard(t.res2))1052deferred_decline = TRUE;1053if (t.err == e_ERANGE)1054deferred_decline = TRUE;1055}10561057/*1058* Perform the operation1059*/10601061errno = t.in_err == e_EDOM ? EDOM : t.in_err == e_ERANGE ? ERANGE : 0;1062if (t.err == e_0)1063t.err = t.in_err;1064if (t.maybeerr == e_0)1065t.maybeerr = t.in_err;10661067if(t.func->type == t_func) {1068switch(t.func->argtype) {1069case at_d: d_res.f = t.func->func.d_d_ptr(d_arg1.f); break;1070case at_s: s_res.f = t.func->func.s_s_ptr(s_arg1.f); break;1071case at_d2: d_res.f = t.func->func.d2_d_ptr(d_arg1.f, d_arg2.f); break;1072case at_s2: s_res.f = t.func->func.s2_s_ptr(s_arg1.f, s_arg2.f); break;1073case at_di: d_res.f = t.func->func.di_d_ptr(d_arg1.f, d_arg2.i[dmsd]); break;1074case at_si: s_res.f = t.func->func.si_s_ptr(s_arg1.f, s_arg2.i); break;1075case at_dip: d_res.f = t.func->func.dip_d_ptr(d_arg1.f, (int*)&intres); break;1076case at_sip: s_res.f = t.func->func.sip_s_ptr(s_arg1.f, (int*)&intres); break;1077case at_ddp: d_res.f = t.func->func.ddp_d_ptr(d_arg1.f, &d_res2.f); break;1078case at_ssp: s_res.f = t.func->func.ssp_s_ptr(s_arg1.f, &s_res2.f); break;1079default:1080printf("unhandled function: %s\n",t.func->name);1081return test_fail;1082}1083} else {1084/* printf("macro: name=%s, num=%i, s1.i=0x%08x s1.f=%f\n",t.func->name, t.func->macro_name, s_arg1.i, (double)s_arg1.f); */1085switch(t.func->macro_name) {1086case m_isfinite: intres = isfinite(d_arg1.f); break;1087case m_isinf: intres = isinf(d_arg1.f); break;1088case m_isnan: intres = isnan(d_arg1.f); break;1089case m_isnormal: intres = isnormal(d_arg1.f); break;1090case m_signbit: intres = signbit(d_arg1.f); break;1091case m_fpclassify: intres = fpclassify(d_arg1.f); break;1092case m_isgreater: intres = isgreater(d_arg1.f, d_arg2.f); break;1093case m_isgreaterequal: intres = isgreaterequal(d_arg1.f, d_arg2.f); break;1094case m_isless: intres = isless(d_arg1.f, d_arg2.f); break;1095case m_islessequal: intres = islessequal(d_arg1.f, d_arg2.f); break;1096case m_islessgreater: intres = islessgreater(d_arg1.f, d_arg2.f); break;1097case m_isunordered: intres = isunordered(d_arg1.f, d_arg2.f); break;10981099case m_isfinitef: intres = isfinite(s_arg1.f); break;1100case m_isinff: intres = isinf(s_arg1.f); break;1101case m_isnanf: intres = isnan(s_arg1.f); break;1102case m_isnormalf: intres = isnormal(s_arg1.f); break;1103case m_signbitf: intres = signbit(s_arg1.f); break;1104case m_fpclassifyf: intres = fpclassify(s_arg1.f); break;1105case m_isgreaterf: intres = isgreater(s_arg1.f, s_arg2.f); break;1106case m_isgreaterequalf: intres = isgreaterequal(s_arg1.f, s_arg2.f); break;1107case m_islessf: intres = isless(s_arg1.f, s_arg2.f); break;1108case m_islessequalf: intres = islessequal(s_arg1.f, s_arg2.f); break;1109case m_islessgreaterf: intres = islessgreater(s_arg1.f, s_arg2.f); break;1110case m_isunorderedf: intres = isunordered(s_arg1.f, s_arg2.f); break;11111112default:1113printf("unhandled macro: %s\n",t.func->name);1114return test_fail;1115}1116}11171118/*1119* Decline the test if the deferred decline flag was set above.1120*/1121if (deferred_decline)1122return test_decline;11231124/* printf("intres=%i\n",intres); */11251126/* Clear the fail text (indicating a pass unless we change it) */1127failp[0] = '\0';11281129/* Check the IEEE status bits (except INX, which we disregard).1130* We don't bother with this for complex numbers, because the1131* complex functions are hard to get exactly right and we don't1132* have to anyway (C99 annex G is only informative). */1133if (!(is_complex_argtype(t.func->argtype) || is_complex_rettype(t.func->rettype))) {1134status = fetestexcept(FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW|FE_UNDERFLOW);1135if ((status|t.maybestatus|~statusmask) != (t.status|t.maybestatus|~statusmask)) {1136if (quiet) failtext[0]='x';1137else {1138failp += sprintf(failp,1139" wrongstatus=%s%s%s%s%s",1140(status & FE_INVALID ? "i" : ""),1141(status & FE_DIVBYZERO ? "z" : ""),1142(status & FE_OVERFLOW ? "o" : ""),1143(status & FE_UNDERFLOW ? "u" : ""),1144(status ? "" : "OK"));1145}1146}1147}11481149/* Check the result */1150{1151unsigned resultr[2], resulti[2];1152unsigned tresultr[3], tresulti[3], wres;11531154switch(t.func->rettype) {1155case rt_d:1156case rt_d2:1157tresultr[0] = t.resultr[0];1158tresultr[1] = t.resultr[1];1159resultr[0] = d_res.i[dmsd]; resultr[1] = d_res.i[dlsd];1160resulti[0] = resulti[1] = 0;1161wres = 2;1162break;1163case rt_i:1164tresultr[0] = t.resultr[0];1165resultr[0] = intres;1166resulti[0] = 0;1167wres = 1;1168break;1169case rt_s:1170case rt_s2:1171tresultr[0] = t.resultr[0];1172resultr[0] = s_res.i;1173resulti[0] = 0;1174wres = 1;1175break;1176default:1177puts("unhandled rettype in runtest");1178abort ();1179}1180if(t.resultc != rc_none) {1181int err = 0;1182switch(t.resultc) {1183case rc_zero:1184if(resultr[0] != 0 || resulti[0] != 0 ||1185(wres==2 && (resultr[1] != 0 || resulti[1] != 0))) {1186err = 1;1187}1188break;1189case rc_infinity:1190if(wres==1) {1191if(!((resultr[0]&0x7fffffff)==0x7f800000 ||1192(resulti[0]&0x7fffffff)==0x7f800000)) {1193err = 1;1194}1195} else {1196if(!(((resultr[0]&0x7fffffff)==0x7ff00000 && resultr[1]==0) ||1197((resulti[0]&0x7fffffff)==0x7ff00000 && resulti[1]==0))) {1198err = 1;1199}1200}1201break;1202case rc_nan:1203if(wres==1) {1204if(!((resultr[0]&0x7fffffff)>0x7f800000 ||1205(resulti[0]&0x7fffffff)>0x7f800000)) {1206err = 1;1207}1208} else {1209canon_dNaN(resultr);1210canon_dNaN(resulti);1211if(!(((resultr[0]&0x7fffffff)>0x7ff00000 && resultr[1]==1) ||1212((resulti[0]&0x7fffffff)>0x7ff00000 && resulti[1]==1))) {1213err = 1;1214}1215}1216break;1217case rc_finite:1218if(wres==1) {1219if(!((resultr[0]&0x7fffffff)<0x7f800000 ||1220(resulti[0]&0x7fffffff)<0x7f800000)) {1221err = 1;1222}1223} else {1224if(!((resultr[0]&0x7fffffff)<0x7ff00000 ||1225(resulti[0]&0x7fffffff)<0x7ff00000)) {1226err = 1;1227}1228}1229break;1230default:1231break;1232}1233if(err) {1234print_error(t.func->rettype,resultr,"wrongresultr",&failp);1235print_error(t.func->rettype,resulti,"wrongresulti",&failp);1236}1237} else if (t.nresult > wres) {1238/*1239* The test case data has provided the result to more1240* than double precision. Instead of testing exact1241* equality, we test against our maximum error1242* tolerance.1243*/1244int rshift, ishift;1245long long ulpsr, ulpsi, ulptolerance;12461247tresultr[wres] = t.resultr[wres] << (32-EXTRABITS);1248tresulti[wres] = t.resulti[wres] << (32-EXTRABITS);1249if(strict) {1250ulptolerance = 4096; /* one ulp */1251} else {1252ulptolerance = t.func->tolerance;1253}1254rshift = ishift = 0;1255if (ulptolerance & ABSLOWERBOUND) {1256/*1257* Hack for the lgamma functions, which have an1258* error behaviour that can't conveniently be1259* characterised in pure ULPs. Really, we want to1260* say that the error in lgamma is "at most N ULPs,1261* or at most an absolute error of X, whichever is1262* larger", for appropriately chosen N,X. But since1263* these two functions are the only cases where it1264* arises, I haven't bothered to do it in a nice way1265* in the function table above.1266*1267* (The difficult cases arise with negative input1268* values such that |gamma(x)| is very near to 1; in1269* this situation implementations tend to separately1270* compute lgamma(|x|) and the log of the correction1271* term from the Euler reflection formula, and1272* subtract - which catastrophically loses1273* significance.)1274*1275* As far as I can tell, nobody cares about this:1276* GNU libm doesn't get those cases right either,1277* and OpenCL explicitly doesn't state a ULP error1278* limit for lgamma. So my guess is that this is1279* simply considered acceptable error behaviour for1280* this particular function, and hence I feel free1281* to allow for it here.1282*/1283ulptolerance &= ~ABSLOWERBOUND;1284if (t.op1r[0] & 0x80000000) {1285if (t.func->rettype == rt_d)1286rshift = 0x400 - ((tresultr[0] >> 20) & 0x7ff);1287else if (t.func->rettype == rt_s)1288rshift = 0x80 - ((tresultr[0] >> 23) & 0xff);1289if (rshift < 0)1290rshift = 0;1291}1292}1293if (ulptolerance & PLUSMINUSPIO2) {1294ulptolerance &= ~PLUSMINUSPIO2;1295/*1296* Hack for range reduction, which can reduce1297* borderline cases in the wrong direction, i.e.1298* return a value just outside one end of the interval1299* [-pi/4,+pi/4] when it could have returned a value1300* just inside the other end by subtracting an1301* adjacent multiple of pi/2.1302*1303* We tolerate this, up to a point, because the1304* trigonometric functions making use of the output of1305* rred can cope and because making the range reducer1306* do the exactly right thing in every case would be1307* more expensive.1308*/1309if (wres == 1) {1310/* Upper bound of overshoot derived in rredf.h */1311if ((resultr[0]&0x7FFFFFFF) <= 0x3f494b02 &&1312(resultr[0]&0x7FFFFFFF) > 0x3f490fda &&1313(resultr[0]&0x80000000) != (tresultr[0]&0x80000000)) {1314unsigned long long val;1315val = tresultr[0];1316val = (val << 32) | tresultr[1];1317/*1318* Compute the alternative permitted result by1319* subtracting from the sum of the extended1320* single-precision bit patterns of +pi/4 and1321* -pi/4. This is a horrible hack which only1322* works because we can be confident that1323* numbers in this range all have the same1324* exponent!1325*/1326val = 0xfe921fb54442d184ULL - val;1327tresultr[0] = val >> 32;1328tresultr[1] = (val >> (32-EXTRABITS)) << (32-EXTRABITS);1329/*1330* Also, expect a correspondingly different1331* value of res2 as a result of this change.1332* The adjustment depends on whether we just1333* flipped the result from + to - or vice1334* versa.1335*/1336if (resultr[0] & 0x80000000) {1337res2_adjust = +1;1338} else {1339res2_adjust = -1;1340}1341}1342}1343}1344ulpsr = calc_error(resultr, tresultr, rshift, t.func->rettype);1345if(is_complex_rettype(t.func->rettype)) {1346ulpsi = calc_error(resulti, tresulti, ishift, t.func->rettype);1347} else {1348ulpsi = 0;1349}1350unsigned *rr = (ulpsr > ulptolerance || ulpsr < -ulptolerance) ? resultr : NULL;1351unsigned *ri = (ulpsi > ulptolerance || ulpsi < -ulptolerance) ? resulti : NULL;1352/* printf("tolerance=%i, ulpsr=%i, ulpsi=%i, rr=%p, ri=%p\n",ulptolerance,ulpsr,ulpsi,rr,ri); */1353if (rr || ri) {1354if (quiet) failtext[0]='x';1355else {1356print_error(t.func->rettype,rr,"wrongresultr",&failp);1357print_error(t.func->rettype,ri,"wrongresulti",&failp);1358print_ulps(t.func->rettype,rr ? ulpsr : 0, ri ? ulpsi : 0,&failp);1359}1360}1361} else {1362if(is_complex_rettype(t.func->rettype))1363/*1364* Complex functions are not fully supported,1365* this is unreachable, but prevents warnings.1366*/1367abort();1368/*1369* The test case data has provided the result in1370* exactly the output precision. Therefore we must1371* complain about _any_ violation.1372*/1373switch(t.func->rettype) {1374case rt_dc:1375canon_dNaN(tresulti);1376canon_dNaN(resulti);1377if (fo) {1378dnormzero(tresulti);1379dnormzero(resulti);1380}1381/* deliberate fall-through */1382case rt_d:1383canon_dNaN(tresultr);1384canon_dNaN(resultr);1385if (fo) {1386dnormzero(tresultr);1387dnormzero(resultr);1388}1389break;1390case rt_sc:1391canon_sNaN(tresulti);1392canon_sNaN(resulti);1393if (fo) {1394snormzero(tresulti);1395snormzero(resulti);1396}1397/* deliberate fall-through */1398case rt_s:1399canon_sNaN(tresultr);1400canon_sNaN(resultr);1401if (fo) {1402snormzero(tresultr);1403snormzero(resultr);1404}1405break;1406default:1407break;1408}1409if(is_complex_rettype(t.func->rettype)) {1410unsigned *rr, *ri;1411if(resultr[0] != tresultr[0] ||1412(wres > 1 && resultr[1] != tresultr[1])) {1413rr = resultr;1414} else {1415rr = NULL;1416}1417if(resulti[0] != tresulti[0] ||1418(wres > 1 && resulti[1] != tresulti[1])) {1419ri = resulti;1420} else {1421ri = NULL;1422}1423if(rr || ri) {1424if (quiet) failtext[0]='x';1425print_error(t.func->rettype,rr,"wrongresultr",&failp);1426print_error(t.func->rettype,ri,"wrongresulti",&failp);1427}1428} else if (resultr[0] != tresultr[0] ||1429(wres > 1 && resultr[1] != tresultr[1])) {1430if (quiet) failtext[0]='x';1431print_error(t.func->rettype,resultr,"wrongresult",&failp);1432}1433}1434/*1435* Now test res2, for those functions (frexp, modf, rred)1436* which use it.1437*/1438if (t.func->func.ptr == &frexp || t.func->func.ptr == &frexpf ||1439t.func->macro_name == m_rred || t.func->macro_name == m_rredf) {1440unsigned tres2 = t.res2[0];1441if (res2_adjust) {1442/* Fix for range reduction, propagated from further up */1443tres2 = (tres2 + res2_adjust) & 3;1444}1445if (tres2 != intres) {1446if (quiet) failtext[0]='x';1447else {1448failp += sprintf(failp,1449" wrongres2=%08x", intres);1450}1451}1452} else if (t.func->func.ptr == &modf || t.func->func.ptr == &modff) {1453tresultr[0] = t.res2[0];1454tresultr[1] = t.res2[1];1455if (is_double_rettype(t.func->rettype)) {1456canon_dNaN(tresultr);1457resultr[0] = d_res2.i[dmsd];1458resultr[1] = d_res2.i[dlsd];1459canon_dNaN(resultr);1460if (fo) {1461dnormzero(tresultr);1462dnormzero(resultr);1463}1464} else {1465canon_sNaN(tresultr);1466resultr[0] = s_res2.i;1467resultr[1] = s_res2.i;1468canon_sNaN(resultr);1469if (fo) {1470snormzero(tresultr);1471snormzero(resultr);1472}1473}1474if (resultr[0] != tresultr[0] ||1475(wres > 1 && resultr[1] != tresultr[1])) {1476if (quiet) failtext[0]='x';1477else {1478if (is_double_rettype(t.func->rettype))1479failp += sprintf(failp, " wrongres2=%08x.%08x",1480resultr[0], resultr[1]);1481else1482failp += sprintf(failp, " wrongres2=%08x",1483resultr[0]);1484}1485}1486}1487}14881489/* Check errno */1490err = (errno == EDOM ? e_EDOM : errno == ERANGE ? e_ERANGE : e_0);1491if (err != t.err && err != t.maybeerr) {1492if (quiet) failtext[0]='x';1493else {1494failp += sprintf(failp, " wrongerrno=%s expecterrno=%s ", errnos[err], errnos[t.err]);1495}1496}14971498return *failtext ? test_fail : test_pass;1499}15001501int passed, failed, declined;15021503void runtests(char *name, FILE *fp) {1504char testbuf[512], linebuf[512];1505int lineno = 1;1506testdetail test;15071508test.valid = 0;15091510if (verbose) printf("runtests: %s\n", name);1511while (fgets(testbuf, sizeof(testbuf), fp)) {1512int res, print_errno;1513testbuf[strcspn(testbuf, "\r\n")] = '\0';1514strcpy(linebuf, testbuf);1515test = parsetest(testbuf, test);1516print_errno = 0;1517while (test.in_err < test.in_err_limit) {1518res = runtest(test);1519if (res == test_pass) {1520if (verbose)1521printf("%s:%d: pass\n", name, lineno);1522++passed;1523} else if (res == test_decline) {1524if (verbose)1525printf("%s:%d: declined\n", name, lineno);1526++declined;1527} else if (res == test_fail) {1528if (!quiet)1529printf("%s:%d: FAIL%s: %s%s%s%s\n", name, lineno,1530test.random ? " (random)" : "",1531linebuf,1532print_errno ? " errno_in=" : "",1533print_errno ? errnos[test.in_err] : "",1534failtext);1535++failed;1536} else if (res == test_invalid) {1537printf("%s:%d: malformed: %s\n", name, lineno, linebuf);1538++failed;1539}1540test.in_err++;1541print_errno = 1;1542}1543lineno++;1544}1545}15461547int main(int ac, char **av) {1548char **files;1549int i, nfiles = 0;1550dbl d;15511552#ifdef MICROLIB1553/*1554* Invent argc and argv ourselves.1555*/1556char *argv[256];1557char args[256];1558{1559int sargs[2];1560char *p;15611562ac = 0;15631564sargs[0]=(int)args;1565sargs[1]=(int)sizeof(args);1566if (!__semihost(0x15, sargs)) {1567args[sizeof(args)-1] = '\0'; /* just in case */1568p = args;1569while (1) {1570while (*p == ' ' || *p == '\t') p++;1571if (!*p) break;1572argv[ac++] = p;1573while (*p && *p != ' ' && *p != '\t') p++;1574if (*p) *p++ = '\0';1575}1576}15771578av = argv;1579}1580#endif15811582/* Sort tfuncs */1583qsort(tfuncs, sizeof(tfuncs)/sizeof(test_func), sizeof(test_func), &compare_tfuncs);15841585/*1586* Autodetect the `double' endianness.1587*/1588dmsd = 0;1589d.f = 1.0; /* 0x3ff00000 / 0x00000000 */1590if (d.i[dmsd] == 0) {1591dmsd = 1;1592}1593/*1594* Now dmsd denotes what the compiler thinks we're at. Let's1595* check that it agrees with what the runtime thinks.1596*/1597d.i[0] = d.i[1] = 0x11111111;/* a random +ve number */1598d.f /= d.f; /* must now be one */1599if (d.i[dmsd] == 0) {1600fprintf(stderr, "YIKES! Compiler and runtime disagree on endianness"1601" of `double'. Bailing out\n");1602return 1;1603}1604dlsd = !dmsd;16051606/* default is terse */1607verbose = 0;1608fo = 0;1609strict = 0;16101611files = (char **)malloc((ac+1) * sizeof(char *));1612if (!files) {1613fprintf(stderr, "initial malloc failed!\n");1614return 1;1615}1616#ifdef NOCMDLINE1617files[nfiles++] = "testfile";1618#endif16191620while (--ac) {1621char *p = *++av;1622if (*p == '-') {1623static char *options[] = {1624"-fo",1625#if 01626"-noinexact",1627"-noround",1628#endif1629"-nostatus",1630"-quiet",1631"-strict",1632"-v",1633"-verbose",1634};1635enum {1636op_fo,1637#if 01638op_noinexact,1639op_noround,1640#endif1641op_nostatus,1642op_quiet,1643op_strict,1644op_v,1645op_verbose,1646};1647switch (find(p, options, sizeof(options))) {1648case op_quiet:1649quiet = 1;1650break;1651#if 01652case op_noinexact:1653statusmask &= 0x0F; /* remove bit 4 */1654break;1655case op_noround:1656doround = 0;1657break;1658#endif1659case op_nostatus: /* no status word => noinx,noround */1660statusmask = 0;1661doround = 0;1662break;1663case op_v:1664case op_verbose:1665verbose = 1;1666break;1667case op_fo:1668fo = 1;1669break;1670case op_strict: /* tolerance is 1 ulp */1671strict = 1;1672break;1673default:1674fprintf(stderr, "unrecognised option: %s\n", p);1675break;1676}1677} else {1678files[nfiles++] = p;1679}1680}16811682passed = failed = declined = 0;16831684if (nfiles) {1685for (i = 0; i < nfiles; i++) {1686FILE *fp = fopen(files[i], "r");1687if (!fp) {1688fprintf(stderr, "Couldn't open %s\n", files[i]);1689} else1690runtests(files[i], fp);1691}1692} else1693runtests("(stdin)", stdin);16941695printf("Completed. Passed %d, failed %d (total %d",1696passed, failed, passed+failed);1697if (declined)1698printf(" plus %d declined", declined);1699printf(")\n");1700if (failed || passed == 0)1701return 1;1702printf("** TEST PASSED OK **\n");1703return 0;1704}17051706void undef_func() {1707failed++;1708puts("ERROR: undefined function called");1709}1710/* clang-format on */171117121713