chiark / gitweb /
@@@ fltfmt wip
[mLib] / utils / t / fltfmt-test.c
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
44 enum {
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
64 static 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 };
74 static const struct tvec_flaginfo flterr_flaginfo =
75   { "flterr", flterr_flags, &tvrange_uint };
76 static const struct tvec_flaginfo flterrmask_flaginfo =
77   { "flterrmask", flterr_flags + 1, &tvrange_uint };
78
79 static const struct tvec_flag fltrnd_flags[] = {
80
81   /* Standard rounding modes. */
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 },
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    */
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 },
121
122   /* Phew! */
123   TVEC_ENDFLAGS
124 };
125 static const struct tvec_flaginfo fltrnd_flaginfo =
126   { "fltrnd", fltrnd_flags, &tvrange_u16 };
127
128 static 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 };
136 static const struct tvec_flaginfo floatbits_flaginfo =
137   { "floatbits-flags", floatbits_flags, &tvrange_uint };
138
139 static 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
154 static 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
168 static 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
185 static 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
198 static 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
208 static const struct tvec_test round_test =
209   { "round", round_regs, 0, test_round };
210
211 /*----- IEEE format conversions -------------------------------------------*/
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 };
277 IEEE_FORMATS(DEF_RANGE)
278 #undef DEF_RANGE
279
280 static 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 }
286 static struct tvec_env encieee_env = { 0, 0, 0, before_encieee, 0, 0, 0 };
287
288 static void before_decieee(struct tvec_state *tv, void *ctx)
289   { DEFAULT_REG(RERR_OUT, rv->u = FLTERR_OK); }
290 static 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
344 IEEE_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
351 /*----- Native format conversions -----------------------------------------*/
352
353 #define NATIVE_FORMATS(_)                                               \
354   _(flt, float, FLT)                                                    \
355   _(dbl, double, DBL)
356
357 enum {
358 #define DEF_ENUM(ty, cty, TY) NTV_##TY,
359   NATIVE_FORMATS(DEF_ENUM)
360 #undef DEF_ENUM
361   NTV_NFMT
362 };
363
364 static 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
382 static 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 };
393 static const struct tvec_flaginfo assume_flaginfo =
394   { "assume", assume_flags, &tvrange_uint };
395
396 struct assumeenv { struct tvec_env _env; unsigned ntv; };
397 struct assumectx { unsigned af, want; };
398
399 static void setup_assume(struct tvec_state *tv, const struct tvec_env *env,
400                          void *pctx, void *ctx)
401 {
402   const struct assumeenv *aenv = (const struct assumeenv *)env;
403   const struct ntvinfo *info = &ntvinfo[aenv->ntv];
404   struct assumectx *actx = ctx;
405   double prec;
406
407   switch (info->fmt&(FLTFMT_ORGMASK | FLTFMT_TYPEMASK)) {
408     case FLTFMT_IEEE_F32:
409       actx->af = AF_NEGZ | AF_INF | AF_IEEE | AF_PREC24;
410       break;
411     case FLTFMT_IEEE_F64:
412       actx->af = AF_NEGZ | AF_INF | AF_IEEE | AF_PREC24 | AF_PREC53;
413       break;
414     case FLTFMT_IEEE_F128:
415       actx->af = AF_NEGZ | AF_INF | AF_IEEE |
416                  AF_PREC24 | AF_PREC53 | AF_PREC64 | AF_PREC113;
417       break;
418     case FLTFMT_INTEL_F80:
419       actx->af = AF_NEGZ | AF_INF | AF_IEEE |
420                  AF_PREC24 | AF_PREC53 | AF_PREC64;
421       break;
422     default:
423       actx->af = 0;
424       if (NEGP(-0.0)) actx->af |= AF_NEGZ;
425 #ifdef INF
426       actx->af |= AF_INF;
427 #endif
428 #ifdef NAN
429       actx->af |= AF_STDCNAN;
430 #endif
431       prec = log(FLT_RADIX)/log(2.0)*info->mant_dig;
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;
436       break;
437   }
438   actx->want = 0;
439 }
440
441 static int setvar_assume(struct tvec_state *tv, const char *var,
442                          const union tvec_regval *rv, void *ctx)
443 {
444   struct assumectx *actx = ctx;
445
446   if (STRCMP(var, ==, "@assume")) actx->want = rv->u;
447   else return (tvec_unkregerr(tv, var));
448   return (0);
449 }
450 static const struct tvec_vardef assume_vardef =
451   { sizeof(struct tvec_reg), setvar_assume,
452     { "@assume", &tvty_flags, 0, 0, { &assume_flaginfo } }};
453 static const struct tvec_vardef *findvar_assume
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
462 static void before_assume(struct tvec_state *tv, void *ctx)
463 {
464   struct assumectx *actx = ctx;
465
466   if ((tv->f&TVSF_ACTIVE) && (actx->want&~actx->af))
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
474 static void after_assume(struct tvec_state *tv, void *ctx)
475 {
476   struct assumectx *actx = ctx;
477
478   actx->want = 0;
479 }
480
481 #define DEF_TEST(ty, cty, TY)                                           \
482                                                                         \
483   static struct assumeenv ty##_env =                                    \
484     { { sizeof(struct assumectx),                                       \
485         setup_assume, findvar_assume, before_assume, 0, after_assume, 0 }, \
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
534 NATIVE_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
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
574 DIRECT_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
607 DIRECT_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
621 /*----- Main code ---------------------------------------------------------*/
622
623 static const struct tvec_test *const tests[] = {
624   &round_test,
625   NATIVE_TESTS
626   IEEE_TESTS
627   DIRECT_TESTS
628   0
629 };
630
631 static const struct tvec_config testconfig =
632   { tests, NROUT, NREG, sizeof(struct tvec_reg) };
633
634 int main(int argc, char *argv[])
635   { return (tvec_main(argc, argv, &testconfig, 0)); }
636
637 /*----- That's all, folks -------------------------------------------------*/