3 * Test for floating-point conversions
5 * (c) 2024 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of the mLib utilities library.
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.
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.
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,
28 /*----- Header files ------------------------------------------------------*/
39 #include "tvec-remote.h"
40 #include "tvec-types.h"
42 /*----- Register allocation -----------------------------------------------*/
46 RF_OUT
, RZ_OUT
= RF_OUT
,
62 /*----- Type definitions --------------------------------------------------*/
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
},
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
};
79 static const struct tvec_flag fltrnd_flags
[] = {
81 /* Standard rounding modes. */
82 { "zero", 0xffff, FLTRND_ZERO
},
83 { "projinf", 0xffff, FLTRND_PROJINF
},
84 { "posinf", 0xffff, FLTRND_POSINF
},
85 { "neginf", 0xffff, FLTRND_NEGINF
},
86 { "odd", 0xffff, FLTRND_ODD
},
87 { "even", 0xffff, FLTRND_EVEN
},
88 { "nearest-even", 0xffff, FLTRND_NEAREVEN
},
89 { "nearest-odd", 0xffff, FLTRND_NEARODD
},
90 { "nearest-zero", 0xffff, FLTRND_NEARZERO
},
91 { "nearest-projinf", 0xffff, FLTRND_NEARINF
},
92 { "nearest-neginf", 0xffff, FLTRND_NEARNEG
},
93 { "nearest-posinf", 0xffff, FLTRND_NEARPOS
},
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
100 * <s> @NEG@ %|+|% %|-|%
101 * <u> @ODD@ %|0|% %|1|%
102 * <h> @HALF@ %|0|% %|1|%
103 * <r> @LOW@ %|0|% %|1|%
105 { "+0.00", 0x0001, 0x0001 },
106 { "+0.01", 0x0002, 0x0002 },
107 { "+0.10", 0x0004, 0x0004 },
108 { "+0.11", 0x0008, 0x0008 },
109 { "+1.00", 0x0010, 0x0010 },
110 { "+1.01", 0x0020, 0x0020 },
111 { "+1.10", 0x0040, 0x0040 },
112 { "+1.11", 0x0080, 0x0080 },
113 { "-0.00", 0x0100, 0x0100 },
114 { "-0.01", 0x0200, 0x0200 },
115 { "-0.10", 0x0400, 0x0400 },
116 { "-0.11", 0x0800, 0x0800 },
117 { "-1.00", 0x1000, 0x1000 },
118 { "-1.01", 0x2000, 0x2000 },
119 { "-1.10", 0x4000, 0x4000 },
120 { "-1.11", 0x8000, 0x8000 },
125 static const struct tvec_flaginfo fltrnd_flaginfo
=
126 { "fltrnd", fltrnd_flags
, &tvrange_u16
};
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
},
136 static const struct tvec_flaginfo floatbits_flaginfo
=
137 { "floatbits-flags", floatbits_flags
, &tvrange_uint
};
139 static const struct tvec_urange frac_range
= { 0, UINT_MAX
, 4, 0 };
141 /*----- Test utilities ----------------------------------------------------*/
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; \
148 if (!(_inr->f&TVRF_LIVE)) { \
149 set; _inr->f |= TVRF_LIVE; \
150 if (_rix < NROUT) _outr->f |= TVRF_LIVE; \
154 static void get_floatbits(struct floatbits
*z_out
, const struct tvec_reg
*in
)
156 const unsigned char *p
;
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
);
168 static void put_floatbits(struct tvec_reg
*out
, const struct floatbits
*x
)
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
;
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
]);
180 out
[RM_OUT
].f
|= TVRF_LIVE
;
183 /*----- Utilities ---------------------------------------------------------*/
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
} },
198 static void test_round(const struct tvec_reg
*in
, struct tvec_reg
*out
,
201 struct floatbits x
= FLOATBITS_INIT
, z
= FLOATBITS_INIT
;
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
);
208 static const struct tvec_test round_test
=
209 { "round", round_regs
, 0, test_round
};
211 /*----- IEEE format conversion --------------------------------------------*/
213 #define IEEE_FORMATS(_) \
222 #define DECLS_mini octet y = 0
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
229 #define DECLS_bf16 uint16 y = 0
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
236 #define DECLS_f16 uint16 y = 0
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
243 #define DECLS_f32 uint32 y = 0
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
250 #define DECLS_f64 kludge64 y = { 0 }
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
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
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
275 #define DEF_RANGE(ty, sz) \
276 static const struct tvec_urange ty##_range = { sz, sz, 0, 0 };
277 IEEE_FORMATS(DEF_RANGE
)
280 static void before_encieee(struct tvec_state
*tv
, void *ctx
)
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
);
286 static struct tvec_env encieee_env
= { 0, 0, 0, before_encieee
, 0, 0, 0 };
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 };
292 #define DEF_TEST(ty, sz) \
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 } }, \
306 static void test_enc##ty(const struct tvec_reg *in, struct tvec_reg *out, \
309 struct floatbits x = FLOATBITS_INIT; \
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); \
319 static const struct tvec_test enc##ty##_test = \
320 { "enc" #ty, enc##ty##_regs, &encieee_env, test_enc##ty }; \
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 } }, \
331 static void test_dec##ty(const struct tvec_reg *in, struct tvec_reg *out, \
334 struct floatbits z = FLOATBITS_INIT; \
337 LOAD_##ty; out[RERR_OUT].v.u = fltfmt_dec##ty(&z, DECARGS_##ty); \
338 put_floatbits(out, &z); fltfmt_freebits(&z); \
341 static const struct tvec_test dec##ty##_test = \
342 { "dec" #ty, dec##ty##_regs, &decieee_env, test_dec##ty };
344 IEEE_FORMATS(DEF_TEST
)
348 #define DEF_IEEE_TEST(ty, sz) &enc##ty##_test, &dec##ty##_test,
349 #define IEEE_TESTS IEEE_FORMATS(DEF_IEEE_TEST)
351 /*----- Native format conversion ------------------------------------------*/
353 #define NATIVE_FORMATS(_) \
358 #define DEF_ENUM(ty, cty, TY) NTV_##TY,
359 NATIVE_FORMATS(DEF_ENUM
)
364 static const struct ntvinfo
{
368 #define DEF_INFO(ty, cty, TY) { TY##_FORMAT, TY##_MANT_DIG },
369 NATIVE_FORMATS(DEF_INFO
)
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
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
},
393 static const struct tvec_flaginfo assume_flaginfo
=
394 { "assume", assume_flags
, &tvrange_uint
};
396 struct nativeenv
{ struct tvec_env _env
; unsigned ntv
; };
397 struct nativectx
{ unsigned af
, want
; };
399 static void setup_native(struct tvec_state
*tv
, const struct tvec_env
*env
,
400 void *pctx
, void *ctx
)
402 const struct nativeenv
*nenv
= (const struct nativeenv
*)env
;
403 const struct ntvinfo
*info
= &ntvinfo
[nenv
->ntv
];
404 struct nativectx
*nctx
= ctx
;
407 switch (info
->fmt
&(FLTFMT_ORGMASK
| FLTFMT_TYPEMASK
)) {
408 case FLTFMT_IEEE_F32
:
409 nctx
->af
= AF_NEGZ
| AF_INF
| AF_IEEE
| AF_PREC24
;
411 case FLTFMT_IEEE_F64
:
412 nctx
->af
= AF_NEGZ
| AF_INF
| AF_IEEE
| AF_PREC24
| AF_PREC53
;
414 case FLTFMT_IEEE_F128
:
415 nctx
->af
= AF_NEGZ
| AF_INF
| AF_IEEE
|
416 AF_PREC24
| AF_PREC53
| AF_PREC64
| AF_PREC113
;
418 case FLTFMT_INTEL_F80
:
419 nctx
->af
= AF_NEGZ
| AF_INF
| AF_IEEE
|
420 AF_PREC24
| AF_PREC53
| AF_PREC64
;
424 if (NEGP(-0.0)) nctx
->af
|= AF_NEGZ
;
429 nctx
->af
|= AF_STDCNAN
;
431 prec
= log(FLT_RADIX
)/log(2.0)*info
->mant_dig
;
432 if (prec
>= 24) nctx
->af
|= AF_PREC24
;
433 if (prec
>= 53) nctx
->af
|= AF_PREC53
;
434 if (prec
>= 64) nctx
->af
|= AF_PREC64
;
435 if (prec
>= 113) nctx
->af
|= AF_PREC113
;
441 static int setvar_native(struct tvec_state
*tv
, const char *var
,
442 const union tvec_regval
*rv
, void *ctx
)
444 struct nativectx
*nctx
= ctx
;
446 if (STRCMP(var
, ==, "@assume")) nctx
->want
= rv
->u
;
447 else return (tvec_unkregerr(tv
, var
));
450 static const struct tvec_vardef assume_vardef
=
451 { sizeof(struct tvec_reg
), setvar_native
,
452 { "@assume", &tvty_flags
, 0, 0, { &assume_flaginfo
} }};
453 static const struct tvec_vardef
*findvar_native
454 (struct tvec_state
*tv
, const char *name
, void **ctx_out
, void *ctx
)
456 if (STRCMP(name
, ==, "@assume"))
457 { *ctx_out
= ctx
; return (&assume_vardef
); }
462 static void before_native(struct tvec_state
*tv
, void *ctx
)
464 struct nativectx
*nctx
= ctx
;
466 if (nctx
->want
&~nctx
->af
)
467 tvec_skip(tv
, "unsatisfied assumption");
469 DEFAULT_REG(RROUND
, rv
->u
= FLTRND_NEAREVEN
);
470 DEFAULT_REG(RERR_OUT
, rv
->u
= FLTERR_OK
);
474 static void after_native(struct tvec_state
*tv
, void *ctx
)
476 struct nativectx
*nctx
= ctx
;
481 #define DEF_TEST(ty, cty, TY) \
483 static struct nativeenv ty##_env = \
484 { { sizeof(struct nativectx), \
485 setup_native, findvar_native, before_native, 0, after_native, 0 }, \
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 } }, \
498 static void test_enc##ty(const struct tvec_reg *in, struct tvec_reg *out, \
501 struct floatbits x = FLOATBITS_INIT; \
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); \
509 static const struct tvec_test enc##ty##_test = \
510 { "enc" #ty, enc##ty##_regs, &ty##_env._env, test_enc##ty }; \
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 } }, \
522 static void test_dec##ty(const struct tvec_reg *in, struct tvec_reg *out, \
525 struct floatbits z = FLOATBITS_INIT; \
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); \
531 static const struct tvec_test dec##ty##_test = \
532 { "dec" #ty, dec##ty##_regs, &ty##_env._env, test_dec##ty };
534 NATIVE_FORMATS(DEF_TEST
)
538 #define DEF_NATIVE_TEST(ty, cty, TY) &enc##ty##_test, &dec##ty##_test,
539 #define NATIVE_TESTS NATIVE_FORMATS(DEF_NATIVE_TEST)
541 /*----- Main code ---------------------------------------------------------*/
543 static const struct tvec_test
*const tests
[] = {
550 static const struct tvec_config testconfig
=
551 { tests
, NROUT
, NREG
, sizeof(struct tvec_reg
) };
553 int main(int argc
, char *argv
[])
554 { return (tvec_main(argc
, argv
, &testconfig
, 0)); }
556 /*----- That's all, folks -------------------------------------------------*/