@@@ fltfmt wip
[mLib] / utils / t / fltfmt-test.c
CommitLineData
b1a20bee
MW
1/* -*-c-*-
2 *
3 * Test for floating-point conversions
4 *
5 * (c) 2024 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the mLib utilities library.
11 *
12 * mLib is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU Library General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or (at
15 * your option) any later version.
16 *
17 * mLib is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with mLib. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25 * USA.
26 */
27
28/*----- Header files ------------------------------------------------------*/
29
30#include "config.h"
31
32#include <float.h>
33#include <limits.h>
34#include <math.h>
35
36#include "fltfmt.h"
37#include "maths.h"
38#include "tvec.h"
39#include "tvec-remote.h"
40#include "tvec-types.h"
41
42/*----- Register allocation -----------------------------------------------*/
43
44enum {
45 RERR_OUT,
46 RF_OUT, RZ_OUT = RF_OUT,
47 RE_OUT,
48 RM_OUT,
49 NROUT,
50
51 RF = NROUT, RX = RF,
52 RE,
53 RM,
54
55 RN,
56 RROUND,
57 RERRMASK,
58
59 NREG
60};
61
62/*----- Type definitions --------------------------------------------------*/
63
64static const struct tvec_flag flterr_flags[] = {
65 { "OK", FLTERR_ALLERRS, FLTERR_OK },
66 { "ALLERRS", FLTERR_ALLERRS, FLTERR_ALLERRS },
67 { "INVAL", FLTERR_INVAL, FLTERR_INVAL },
68 { "INEXACT", FLTERR_INEXACT, FLTERR_INEXACT },
69 { "UFLOW", FLTERR_UFLOW, FLTERR_UFLOW },
70 { "OFLOW", FLTERR_OFLOW, FLTERR_OFLOW },
71 { "REPR", FLTERR_REPR, FLTERR_REPR },
72 TVEC_ENDFLAGS
73};
74static const struct tvec_flaginfo flterr_flaginfo =
75 { "flterr", flterr_flags, &tvrange_uint };
76static const struct tvec_flaginfo flterrmask_flaginfo =
77 { "flterrmask", flterr_flags + 1, &tvrange_uint };
78
79static const struct tvec_flag fltrnd_flags[] = {
80
81 /* Standard rounding modes. */
289651a7
MW
82 { "zero", 0xffffu, FLTRND_ZERO },
83 { "projinf", 0xffffu, FLTRND_PROJINF },
84 { "posinf", 0xffffu, FLTRND_POSINF },
85 { "neginf", 0xffffu, FLTRND_NEGINF },
86 { "odd", 0xffffu, FLTRND_ODD },
87 { "even", 0xffffu, FLTRND_EVEN },
88 { "nearest-even", 0xffffu, FLTRND_NEAREVEN },
89 { "nearest-odd", 0xffffu, FLTRND_NEARODD },
90 { "nearest-zero", 0xffffu, FLTRND_NEARZERO },
91 { "nearest-projinf", 0xffffu, FLTRND_NEARINF },
92 { "nearest-neginf", 0xffffu, FLTRND_NEARNEG },
93 { "nearest-posinf", 0xffffu, FLTRND_NEARPOS },
b1a20bee
MW
94
95 /* Rounding mode bits: rounds away from zero in the listed conditions.
96 * The notation corresponds to rounding predicates as follows. The syntax
97 * is `<s><u>.<h><r>' where
98 *
99 * var bit clear set
100 * <s> @NEG@ %|+|% %|-|%
101 * <u> @ODD@ %|0|% %|1|%
102 * <h> @HALF@ %|0|% %|1|%
103 * <r> @LOW@ %|0|% %|1|%
104 */
289651a7
MW
105 { "+0.00", 0x0001u, 0x0001u },
106 { "+0.01", 0x0002u, 0x0002u },
107 { "+0.10", 0x0004u, 0x0004u },
108 { "+0.11", 0x0008u, 0x0008u },
109 { "+1.00", 0x0010u, 0x0010u },
110 { "+1.01", 0x0020u, 0x0020u },
111 { "+1.10", 0x0040u, 0x0040u },
112 { "+1.11", 0x0080u, 0x0080u },
113 { "-0.00", 0x0100u, 0x0100u },
114 { "-0.01", 0x0200u, 0x0200u },
115 { "-0.10", 0x0400u, 0x0400u },
116 { "-0.11", 0x0800u, 0x0800u },
117 { "-1.00", 0x1000u, 0x1000u },
118 { "-1.01", 0x2000u, 0x2000u },
119 { "-1.10", 0x4000u, 0x4000u },
120 { "-1.11", 0x8000u, 0x8000u },
b1a20bee
MW
121
122 /* Phew! */
123 TVEC_ENDFLAGS
124};
125static const struct tvec_flaginfo fltrnd_flaginfo =
126 { "fltrnd", fltrnd_flags, &tvrange_u16 };
127
128static const struct tvec_flag floatbits_flags[] = {
129 { "NEG", FLTF_NEG, FLTF_NEG },
130 { "INF", FLTF_INF, FLTF_INF },
131 { "QNAN", FLTF_QNAN, FLTF_QNAN },
132 { "SNAN", FLTF_SNAN, FLTF_SNAN },
133 { "ZERO", FLTF_ZERO, FLTF_ZERO },
134 TVEC_ENDFLAGS
135};
136static const struct tvec_flaginfo floatbits_flaginfo =
137 { "floatbits-flags", floatbits_flags, &tvrange_uint };
138
139static const struct tvec_urange frac_range = { 0, UINT_MAX, 4, 0 };
140
141/*----- Test utilities ----------------------------------------------------*/
142
143#define DEFAULT_REG(rix, set) do { \
144 unsigned _rix = (rix); \
145 struct tvec_reg *_inr = &tv->in[_rix], *_outr = &tv->out[_rix]; \
146 union tvec_regval *rv = &_inr->v; \
147 \
148 if (!(_inr->f&TVRF_LIVE)) { \
149 set; _inr->f |= TVRF_LIVE; \
150 if (_rix < NROUT) _outr->f |= TVRF_LIVE; \
151 } \
152} while (0)
153
154static void get_floatbits(struct floatbits *z_out, const struct tvec_reg *in)
155{
156 const unsigned char *p;
157 unsigned i, n;
158
159 z_out->f = in[RF].v.u;
160 if (in[RE].f&TVRF_LIVE) z_out->exp = in[RE].v.i;
161 if (in[RM].f&TVRF_LIVE) {
162 n = in[RM].v.bytes.sz/4; fltfmt_allocfrac(z_out, n);
163 for (p = in[RM].v.bytes.p, i = 0; i < n; p += 4, i++)
164 z_out->frac[i] = LOAD32(p);
165 }
166}
167
168static void put_floatbits(struct tvec_reg *out, const struct floatbits *x)
169{
170 unsigned char *p;
171 unsigned i;
172
173 out[RF_OUT].v.u = x->f; out[RF_OUT].f |= TVRF_LIVE;
174 out[RE_OUT].v.i = x->exp; out[RE_OUT].f |= TVRF_LIVE;
175 if (x->n) {
176 tvec_allocbytes(&out[RM_OUT].v, 4*x->n);
177 for (p = out[RM_OUT].v.bytes.p, i = 0; i < x->n; p += 4, i++)
178 STORE32(p, x->frac[i]);
179 }
180 out[RM_OUT].f |= TVRF_LIVE;
181}
182
183/*----- Utilities ---------------------------------------------------------*/
184
185static const struct tvec_regdef round_regs[] = {
186 { "round", &tvty_flags, RROUND, 0, { &fltrnd_flaginfo } },
187 { "n", &tvty_uint, RN, 0, { &tvrange_uint } },
188 { "f", &tvty_flags, RF, 0, { &floatbits_flaginfo } },
189 { "e", &tvty_int, RE, TVRF_OPT, { &tvrange_int } },
190 { "m", &tvty_bytes, RM, TVRF_OPT, { &frac_range } },
191 { "ff", &tvty_flags, RF_OUT, 0, { &floatbits_flaginfo } },
192 { "ee", &tvty_int, RE_OUT, TVRF_OPT, { &tvrange_int } },
193 { "mm", &tvty_bytes, RM_OUT, TVRF_OPT, { &frac_range } },
194 { "err", &tvty_flags, RERR_OUT, 0, { &flterr_flaginfo } },
195 TVEC_ENDREGS
196};
197
198static void test_round(const struct tvec_reg *in, struct tvec_reg *out,
199 void *ctx)
200{
201 struct floatbits x = FLOATBITS_INIT, z = FLOATBITS_INIT;
202
203 get_floatbits(&x, in);
204 out[RERR_OUT].v.u = fltfmt_round(&z, &x, in[RROUND].v.u, in[RN].v.u);
205 put_floatbits(out, &z); fltfmt_freebits(&x); fltfmt_freebits(&z);
206}
207
208static const struct tvec_test round_test =
209 { "round", round_regs, 0, test_round };
210
dc6eea4e 211/*----- IEEE format conversions -------------------------------------------*/
b1a20bee
MW
212
213#define IEEE_FORMATS(_) \
214 _(mini, 1) \
215 _(bf16, 2) \
216 _(f16, 2) \
217 _(f32, 4) \
218 _(f64, 8) \
219 _(f128, 16) \
220 _(idblext80, 10)
221
222#define DECLS_mini octet y = 0
223#define OUTSZ_mini 1
224#define LOAD_mini y = LOAD8(in[RX].v.bytes.p)
225#define STORE_mini STORE8(out[RZ_OUT].v.bytes.p, y)
226#define ENCARGS_mini &y
227#define DECARGS_mini y
228
229#define DECLS_bf16 uint16 y = 0
230#define OUTSZ_bf16 2
231#define LOAD_bf16 y = LOAD16_B(in[RX].v.bytes.p)
232#define STORE_bf16 STORE16_B(out[RZ_OUT].v.bytes.p, y)
233#define ENCARGS_bf16 &y
234#define DECARGS_bf16 y
235
236#define DECLS_f16 uint16 y = 0
237#define OUTSZ_f16 2
238#define LOAD_f16 y = LOAD16_B(in[RX].v.bytes.p)
239#define STORE_f16 STORE16_B(out[RZ_OUT].v.bytes.p, y)
240#define ENCARGS_f16 &y
241#define DECARGS_f16 y
242
243#define DECLS_f32 uint32 y = 0
244#define OUTSZ_f32 4
245#define LOAD_f32 y = LOAD32_B(in[RX].v.bytes.p)
246#define STORE_f32 STORE32_B(out[RZ_OUT].v.bytes.p, y)
247#define ENCARGS_f32 &y
248#define DECARGS_f32 y
249
250#define DECLS_f64 kludge64 y = { 0 }
251#define OUTSZ_f64 8
252#define LOAD_f64 LOAD64_B_(y, in[RX].v.bytes.p)
253#define STORE_f64 STORE64_B_(out[RZ_OUT].v.bytes.p, y)
254#define ENCARGS_f64 &y
255#define DECARGS_f64 y
256
257#define DECLS_f128 uint32 yv[4] = { 0 }; unsigned i
258#define OUTSZ_f128 16
259#define LOAD_f128 for (i = 0; i < 4; i++) \
260 yv[i] = LOAD32_B(in[RX].v.bytes.p + 4*i)
261#define STORE_f128 for (i = 0; i < 4; i++) \
262 STORE32_B(out[RZ_OUT].v.bytes.p + 4*i, yv[i])
263#define ENCARGS_f128 yv
264#define DECARGS_f128 yv
265
266#define DECLS_idblext80 uint16 se = 0; kludge64 f = { 0 }
267#define OUTSZ_idblext80 10
268#define LOAD_idblext80 se = LOAD16_B(in[RX].v.bytes.p + 0); \
269 LOAD64_B_(f, in[RX].v.bytes.p + 2)
270#define STORE_idblext80 STORE16_B(out[RZ_OUT].v.bytes.p + 0, se); \
271 STORE64_B_(out[RZ_OUT].v.bytes.p + 2, f)
272#define ENCARGS_idblext80 &se, &f
273#define DECARGS_idblext80 se, f
274
275#define DEF_RANGE(ty, sz) \
276 static const struct tvec_urange ty##_range = { sz, sz, 0, 0 };
277IEEE_FORMATS(DEF_RANGE)
278#undef DEF_RANGE
279
280static void before_encieee(struct tvec_state *tv, void *ctx)
281{
282 DEFAULT_REG(RROUND, rv->u = FLTRND_NEAREVEN);
283 DEFAULT_REG(RERRMASK, rv->u = FLTERR_ALLERRS);
284 DEFAULT_REG(RERR_OUT, rv->u = FLTERR_OK);
285}
286static struct tvec_env encieee_env = { 0, 0, 0, before_encieee, 0, 0, 0 };
287
288static void before_decieee(struct tvec_state *tv, void *ctx)
289 { DEFAULT_REG(RERR_OUT, rv->u = FLTERR_OK); }
290static struct tvec_env decieee_env = { 0, 0, 0, before_decieee, 0, 0, 0 };
291
292#define DEF_TEST(ty, sz) \
293 \
294 static const struct tvec_regdef enc##ty##_regs[] = { \
295 { "round", &tvty_flags, RROUND, TVRF_OPT, { &fltrnd_flaginfo } }, \
296 { "errmask", &tvty_flags, RERRMASK, TVRF_OPT, \
297 { &flterrmask_flaginfo } }, \
298 { "f", &tvty_flags, RF, TVRF_OPT, { &floatbits_flaginfo } }, \
299 { "e", &tvty_int, RE, TVRF_OPT, { &tvrange_int } }, \
300 { "m", &tvty_bytes, RM, TVRF_OPT, { &frac_range } }, \
301 { "z", &tvty_bytes, RZ_OUT, 0, { &ty##_range } }, \
302 { "err", &tvty_flags, RERR_OUT, TVRF_OPT, { &flterr_flaginfo } }, \
303 TVEC_ENDREGS \
304 }; \
305 \
306 static void test_enc##ty(const struct tvec_reg *in, struct tvec_reg *out, \
307 void *ctx) \
308 { \
309 struct floatbits x = FLOATBITS_INIT; \
310 DECLS_##ty; \
311 \
312 get_floatbits(&x, in); \
313 out[RERR_OUT].v.u = \
314 fltfmt_enc##ty(ENCARGS_##ty, &x, in[RROUND].v.u, in[RERRMASK].v.u); \
315 tvec_allocbytes(&out[RZ_OUT].v, OUTSZ_##ty); STORE_##ty; \
316 fltfmt_freebits(&x); \
317 } \
318 \
319 static const struct tvec_test enc##ty##_test = \
320 { "enc" #ty, enc##ty##_regs, &encieee_env, test_enc##ty }; \
321 \
322 static const struct tvec_regdef dec##ty##_regs[] = { \
323 { "x", &tvty_bytes, RX, 0, { &ty##_range } }, \
324 { "f", &tvty_flags, RF_OUT, 0, { &floatbits_flaginfo } }, \
325 { "e", &tvty_int, RE_OUT, TVRF_OPT, { &tvrange_int } }, \
326 { "m", &tvty_bytes, RM_OUT, TVRF_OPT, { &frac_range } }, \
327 { "err", &tvty_flags, RERR_OUT, TVRF_OPT, { &flterr_flaginfo } }, \
328 TVEC_ENDREGS \
329 }; \
330 \
331 static void test_dec##ty(const struct tvec_reg *in, struct tvec_reg *out, \
332 void *ctx) \
333 { \
334 struct floatbits z = FLOATBITS_INIT; \
335 DECLS_##ty; \
336 \
337 LOAD_##ty; out[RERR_OUT].v.u = fltfmt_dec##ty(&z, DECARGS_##ty); \
338 put_floatbits(out, &z); fltfmt_freebits(&z); \
339 } \
340 \
341 static const struct tvec_test dec##ty##_test = \
342 { "dec" #ty, dec##ty##_regs, &decieee_env, test_dec##ty };
343
344IEEE_FORMATS(DEF_TEST)
345
346#undef DEF_TEST
347
348#define DEF_IEEE_TEST(ty, sz) &enc##ty##_test, &dec##ty##_test,
349#define IEEE_TESTS IEEE_FORMATS(DEF_IEEE_TEST)
350
dc6eea4e 351/*----- Native format conversions -----------------------------------------*/
b1a20bee
MW
352
353#define NATIVE_FORMATS(_) \
354 _(flt, float, FLT) \
355 _(dbl, double, DBL)
356
357enum {
358#define DEF_ENUM(ty, cty, TY) NTV_##TY,
359 NATIVE_FORMATS(DEF_ENUM)
360#undef DEF_ENUM
361 NTV_NFMT
362};
363
364static const struct ntvinfo {
365 unsigned fmt;
366 unsigned mant_dig;
367} ntvinfo[] = {
368#define DEF_INFO(ty, cty, TY) { TY##_FORMAT, TY##_MANT_DIG },
369 NATIVE_FORMATS(DEF_INFO)
370#undef DEF_INFO
371};
372
373#define AF_NEGZ 0x0001u
374#define AF_INF 0x0002u
375#define AF_STDCNAN 0x0004u
376#define AF_IEEE 0x0008u
377#define AF_PREC24 0x0010u
378#define AF_PREC53 0x0020u
379#define AF_PREC64 0x0040u
380#define AF_PREC113 0x0080u
381
382static const struct tvec_flag assume_flags[] = {
383 { "negz", AF_NEGZ, AF_NEGZ },
384 { "inf", AF_INF, AF_INF },
385 { "stdc-nan", AF_STDCNAN, AF_STDCNAN },
386 { "ieee", AF_IEEE, AF_IEEE },
387 { "prec24", AF_PREC24, AF_PREC24 },
388 { "prec53", AF_PREC53, AF_PREC53 },
389 { "prec64", AF_PREC64, AF_PREC64 },
390 { "prec113", AF_PREC113, AF_PREC113 },
391 TVEC_ENDFLAGS
392};
393static const struct tvec_flaginfo assume_flaginfo =
394 { "assume", assume_flags, &tvrange_uint };
395
dc6eea4e
MW
396struct assumeenv { struct tvec_env _env; unsigned ntv; };
397struct assumectx { unsigned af, want; };
b1a20bee 398
dc6eea4e 399static void setup_assume(struct tvec_state *tv, const struct tvec_env *env,
b1a20bee
MW
400 void *pctx, void *ctx)
401{
dc6eea4e
MW
402 const struct assumeenv *aenv = (const struct assumeenv *)env;
403 const struct ntvinfo *info = &ntvinfo[aenv->ntv];
404 struct assumectx *actx = ctx;
b1a20bee
MW
405 double prec;
406
407 switch (info->fmt&(FLTFMT_ORGMASK | FLTFMT_TYPEMASK)) {
408 case FLTFMT_IEEE_F32:
dc6eea4e 409 actx->af = AF_NEGZ | AF_INF | AF_IEEE | AF_PREC24;
b1a20bee
MW
410 break;
411 case FLTFMT_IEEE_F64:
dc6eea4e 412 actx->af = AF_NEGZ | AF_INF | AF_IEEE | AF_PREC24 | AF_PREC53;
b1a20bee
MW
413 break;
414 case FLTFMT_IEEE_F128:
dc6eea4e 415 actx->af = AF_NEGZ | AF_INF | AF_IEEE |
b1a20bee
MW
416 AF_PREC24 | AF_PREC53 | AF_PREC64 | AF_PREC113;
417 break;
418 case FLTFMT_INTEL_F80:
dc6eea4e 419 actx->af = AF_NEGZ | AF_INF | AF_IEEE |
b1a20bee
MW
420 AF_PREC24 | AF_PREC53 | AF_PREC64;
421 break;
422 default:
dc6eea4e
MW
423 actx->af = 0;
424 if (NEGP(-0.0)) actx->af |= AF_NEGZ;
b1a20bee 425#ifdef INF
dc6eea4e 426 actx->af |= AF_INF;
b1a20bee
MW
427#endif
428#ifdef NAN
dc6eea4e 429 actx->af |= AF_STDCNAN;
b1a20bee
MW
430#endif
431 prec = log(FLT_RADIX)/log(2.0)*info->mant_dig;
dc6eea4e
MW
432 if (prec >= 24) actx->af |= AF_PREC24;
433 if (prec >= 53) actx->af |= AF_PREC53;
434 if (prec >= 64) actx->af |= AF_PREC64;
435 if (prec >= 113) actx->af |= AF_PREC113;
b1a20bee
MW
436 break;
437 }
dc6eea4e 438 actx->want = 0;
b1a20bee
MW
439}
440
dc6eea4e 441static int setvar_assume(struct tvec_state *tv, const char *var,
b1a20bee
MW
442 const union tvec_regval *rv, void *ctx)
443{
dc6eea4e 444 struct assumectx *actx = ctx;
b1a20bee 445
dc6eea4e 446 if (STRCMP(var, ==, "@assume")) actx->want = rv->u;
b1a20bee
MW
447 else return (tvec_unkregerr(tv, var));
448 return (0);
449}
450static const struct tvec_vardef assume_vardef =
dc6eea4e 451 { sizeof(struct tvec_reg), setvar_assume,
b1a20bee 452 { "@assume", &tvty_flags, 0, 0, { &assume_flaginfo } }};
dc6eea4e 453static const struct tvec_vardef *findvar_assume
b1a20bee
MW
454 (struct tvec_state *tv, const char *name, void **ctx_out, void *ctx)
455{
456 if (STRCMP(name, ==, "@assume"))
457 { *ctx_out = ctx; return (&assume_vardef); }
458 else
459 return (0);
460}
461
dc6eea4e 462static void before_assume(struct tvec_state *tv, void *ctx)
b1a20bee 463{
dc6eea4e 464 struct assumectx *actx = ctx;
b1a20bee 465
dc6eea4e 466 if ((tv->f&TVSF_ACTIVE) && (actx->want&~actx->af))
b1a20bee
MW
467 tvec_skip(tv, "unsatisfied assumption");
468 else {
469 DEFAULT_REG(RROUND, rv->u = FLTRND_NEAREVEN);
470 DEFAULT_REG(RERR_OUT, rv->u = FLTERR_OK);
471 }
472}
473
dc6eea4e 474static void after_assume(struct tvec_state *tv, void *ctx)
b1a20bee 475{
dc6eea4e 476 struct assumectx *actx = ctx;
b1a20bee 477
dc6eea4e 478 actx->want = 0;
b1a20bee
MW
479}
480
481#define DEF_TEST(ty, cty, TY) \
482 \
dc6eea4e
MW
483 static struct assumeenv ty##_env = \
484 { { sizeof(struct assumectx), \
485 setup_assume, findvar_assume, before_assume, 0, after_assume, 0 }, \
b1a20bee
MW
486 NTV_##TY }; \
487 \
488 static const struct tvec_regdef enc##ty##_regs[] = { \
489 { "round", &tvty_flags, RROUND, TVRF_OPT, { &fltrnd_flaginfo } }, \
490 { "f", &tvty_flags, RF, 0, { &floatbits_flaginfo } }, \
491 { "e", &tvty_int, RE, TVRF_OPT, { &tvrange_int } }, \
492 { "m", &tvty_bytes, RM, TVRF_OPT, { &frac_range } }, \
493 { "z", &tvty_float, RZ_OUT, 0, { &tvflt_##cty } }, \
494 { "err", &tvty_flags, RERR_OUT, TVRF_OPT, { &flterr_flaginfo } }, \
495 TVEC_ENDREGS \
496 }; \
497 \
498 static void test_enc##ty(const struct tvec_reg *in, struct tvec_reg *out, \
499 void *ctx) \
500 { \
501 struct floatbits x = FLOATBITS_INIT; \
502 cty z; \
503 \
504 get_floatbits(&x, in); \
505 out[RERR_OUT].v.u = fltfmt_enc##ty(&z, &x, in[RROUND].v.u); \
506 out[RZ_OUT].v.f = z; fltfmt_freebits(&x); \
507 } \
508 \
509 static const struct tvec_test enc##ty##_test = \
510 { "enc" #ty, enc##ty##_regs, &ty##_env._env, test_enc##ty }; \
511 \
512 static const struct tvec_regdef dec##ty##_regs[] = { \
513 { "x", &tvty_float, RX, 0, { &tvflt_##cty } }, \
514 { "round", &tvty_flags, RROUND, TVRF_OPT, { &fltrnd_flaginfo } }, \
515 { "f", &tvty_flags, RF_OUT, 0, { &floatbits_flaginfo } }, \
516 { "e", &tvty_int, RE_OUT, TVRF_OPT, { &tvrange_int } }, \
517 { "m", &tvty_bytes, RM_OUT, TVRF_OPT, { &frac_range } }, \
518 { "err", &tvty_flags, RERR_OUT, TVRF_OPT, { &flterr_flaginfo } }, \
519 TVEC_ENDREGS \
520 }; \
521 \
522 static void test_dec##ty(const struct tvec_reg *in, struct tvec_reg *out, \
523 void *ctx) \
524 { \
525 struct floatbits z = FLOATBITS_INIT; \
526 \
527 out[RERR_OUT].v.u = fltfmt_dec##ty(&z, in[RX].v.f, in[RROUND].v.u); \
528 put_floatbits(out, &z); fltfmt_freebits(&z); \
529 } \
530 \
531 static const struct tvec_test dec##ty##_test = \
532 { "dec" #ty, dec##ty##_regs, &ty##_env._env, test_dec##ty };
533
534NATIVE_FORMATS(DEF_TEST)
535
536#undef DEF_TEST
537
538#define DEF_NATIVE_TEST(ty, cty, TY) &enc##ty##_test, &dec##ty##_test,
539#define NATIVE_TESTS NATIVE_FORMATS(DEF_NATIVE_TEST)
540
dc6eea4e
MW
541/*----- Direct conversions ------------------------------------------------*/
542
543#define DIRECT_CONVERSIONS(_) \
544 _(flt, float, f32) \
545 _(dbl, double, f64)
546
547#define DEF_TEST1(ty, cty, fty, e) \
548 static void test_##ty##to##fty##e(const struct tvec_reg *in, \
549 struct tvec_reg *out, \
550 void *ctx) \
551 { \
552 tvec_allocbytes(&out[RZ_OUT].v, OUTSZ_##fty); \
553 out[RERR_OUT].v.u = fltfmt_##ty##to##fty##e(out[RZ_OUT].v.bytes.p, \
554 in[RX].v.f, \
555 in[RROUND].v.u); \
556 } \
557 \
558 static const struct tvec_test ty##to##fty##e##_test = \
559 { #ty "to" #fty #e, ty##to##fty##_regs, &ty##_env._env, \
560 test_##ty##to##fty##e };
561
562#define DEF_TEST(ty, cty, fty) \
563 static const struct tvec_regdef ty##to##fty##_regs[] = { \
564 { "round", &tvty_flags, RROUND, TVRF_OPT, { &fltrnd_flaginfo } }, \
565 { "x", &tvty_float, RX, 0, { &tvflt_##cty } }, \
566 { "z", &tvty_bytes, RZ_OUT, 0, { &fty##_range } }, \
567 { "err", &tvty_flags, RERR_OUT, TVRF_OPT, { &flterr_flaginfo } }, \
568 TVEC_ENDREGS \
569 }; \
570 \
571 DEF_TEST1(ty, cty, fty, l) \
572 DEF_TEST1(ty, cty, fty, b)
573
574DIRECT_CONVERSIONS(DEF_TEST)
575
576#undef DEF_TEST1
577#undef DEF_TEST
578
579#define DEF_TEST1(ty, cty, fty, e) \
580 static void test_##fty##e##to##ty(const struct tvec_reg *in, \
581 struct tvec_reg *out, \
582 void *ctx) \
583 { \
584 cty z; \
585 \
586 out[RERR_OUT].v.u = fltfmt_##fty##e##to##ty(&z, in[RX].v.bytes.p, \
587 in[RROUND].v.u); \
588 out[RZ_OUT].v.f = z; \
589 } \
590 \
591 static const struct tvec_test fty##e##to##ty##_test = \
592 { #fty #e "to" #ty, fty##to##ty##_regs, &ty##_env._env, \
593 test_##fty##e##to##ty };
594
595#define DEF_TEST(ty, cty, fty) \
596 static const struct tvec_regdef fty##to##ty##_regs[] = { \
597 { "round", &tvty_flags, RROUND, TVRF_OPT, { &fltrnd_flaginfo } }, \
598 { "x", &tvty_bytes, RX, 0, { &fty##_range } }, \
599 { "z", &tvty_float, RZ_OUT, 0, { &tvflt_##cty } }, \
600 { "err", &tvty_flags, RERR_OUT, TVRF_OPT, { &flterr_flaginfo } }, \
601 TVEC_ENDREGS \
602 }; \
603 \
604 DEF_TEST1(ty, cty, fty, l) \
605 DEF_TEST1(ty, cty, fty, b)
606
607DIRECT_CONVERSIONS(DEF_TEST)
608
609#undef DEF_TEST1
610#undef DEF_TEST
611
612#define DEF_DIRECT_CTOF_TESTS(ty, cty, fty) \
613 &ty##to##fty##l_test, &ty##to##fty##b_test,
614#define DEF_DIRECT_FTOC_TESTS(ty, cty, fty) \
615 &fty##l##to##ty##_test, &fty##b##to##ty##_test,
616#define DEF_DIRECT_TESTS(ty, cty, fty) \
617 DEF_DIRECT_CTOF_TESTS(ty, cty, fty) \
618 DEF_DIRECT_FTOC_TESTS(ty, cty, fty)
619#define DIRECT_TESTS DIRECT_CONVERSIONS(DEF_DIRECT_TESTS)
620
b1a20bee
MW
621/*----- Main code ---------------------------------------------------------*/
622
623static const struct tvec_test *const tests[] = {
624 &round_test,
625 NATIVE_TESTS
626 IEEE_TESTS
dc6eea4e 627 DIRECT_TESTS
b1a20bee
MW
628 0
629};
630
631static const struct tvec_config testconfig =
632 { tests, NROUT, NREG, sizeof(struct tvec_reg) };
633
634int main(int argc, char *argv[])
635 { return (tvec_main(argc, argv, &testconfig, 0)); }
636
637/*----- That's all, folks -------------------------------------------------*/