Commit | Line | Data |
---|---|---|
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 | ||
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. */ | |
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 | }; | |
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 | ||
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 }; | |
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 | ||
dc6eea4e | 351 | /*----- Native format conversions -----------------------------------------*/ |
b1a20bee MW |
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 | ||
dc6eea4e MW |
396 | struct assumeenv { struct tvec_env _env; unsigned ntv; }; |
397 | struct assumectx { unsigned af, want; }; | |
b1a20bee | 398 | |
dc6eea4e | 399 | static 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 | 441 | static 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 | } | |
450 | static const struct tvec_vardef assume_vardef = | |
dc6eea4e | 451 | { sizeof(struct tvec_reg), setvar_assume, |
b1a20bee | 452 | { "@assume", &tvty_flags, 0, 0, { &assume_flaginfo } }}; |
dc6eea4e | 453 | static 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 | 462 | static 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 | 474 | static 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 | ||
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 | ||
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 | ||
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 | ||
b1a20bee MW |
621 | /*----- Main code ---------------------------------------------------------*/ |
622 | ||
623 | static 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 | ||
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 -------------------------------------------------*/ |