==> tests/ftz.c <==1/* mesh-ref ftz.rs's tests, then golden digests from the Rust over every class of input: the special
2 * values crossed with each other, four sweeps of a million random pairs (any bits, tiny exponents,
3 * products and quotients near the subnormal boundary), and f64 -> f32 converts. Each digest is
4 * FNV-1a 64 over every operation's result bits, and the flushes counted must match too. */
5#include "mesh/ftz.h"
6
7#include <stdio.h>
8
9static int failures;
10#define CHECK(c) \
11 do { \
12 if (!(c)) { \
13 fprintf(stderr, "%s:%d: CHECK(%s) failed\n", __FILE__, __LINE__, #c); \
14 failures++; \
15 } \
16 } while (0)
17
18static const float SUB = 1e-40f; /* subnormal */
19static const float MIN = 1.17549435e-38f;
20
21static float f(uint32_t b) { return mesh_ftz_from_bits(b); }
22static uint32_t bits(float x) { return mesh_ftz_bits(x); }
23
24static void rust_inputs_and_results_flush_sign_preserving(void) {
25 CHECK(bits(mesh_ftz_add(SUB, 0.0f)) == 0);
26 CHECK(bits(mesh_ftz_add(-SUB, -0.0f)) == 0x80000000u);
27 CHECK(mesh_ftz_mul(SUB, 1e30f) == 0.0f);
28 CHECK(bits(mesh_ftz_mul(MIN, 0.5f)) == 0);
29 CHECK(bits(mesh_ftz_mul(-MIN, 0.5f)) == 0x80000000u);
30 CHECK(bits(mesh_ftz_sub(MIN, MIN * 1.5f)) == 0x80000000u);
31 CHECK(mesh_ftz_div(1.0f, SUB) == f(0x7f800000u));
32 CHECK(mesh_ftz_div(1.0f, -SUB) == f(0xff800000u));
33 CHECK(mesh_ftz_eq(mesh_ftz_mul(MIN * 2.0f, 0.5f), MIN));
34 CHECK(mesh_ftz_add(1.5f, 2.25f) == 3.75f);
35 CHECK(bits(mesh_ftz_neg(-SUB)) == 0);
36 CHECK(bits(mesh_ftz_abs(-SUB)) == 0);
37}
38
39static void rust_compares_and_converts_flush(void) {
40 float nan = f(0x7fc00000u), inf = f(0x7f800000u);
41 CHECK(mesh_ftz_eq(SUB, 0.0f) && mesh_ftz_eq(-SUB, 0.0f));
42 CHECK(mesh_ftz_ge(-SUB, 0.0f) && mesh_ftz_le(SUB, 0.0f));
43 CHECK(mesh_ftz_lt(SUB, MIN) && mesh_ftz_is_nan(nan));
44 CHECK(mesh_ftz_to_f64(SUB) == 0.0);
45 CHECK(bits(mesh_ftz_from_f64(1e-40)) == 0);
46 CHECK(bits(mesh_ftz_from_f64(-1e-40)) == 0x80000000u);
47 CHECK(mesh_ftz_from_f64(1e-30) == (float)1e-30);
48 volatile float moved = SUB; /* moves keep the bits: only operations flush */
49 CHECK(bits(moved) == bits(SUB));
50 CHECK(mesh_ftz_is_nan(nan) && mesh_ftz_is_nan(f(0xffc00000u)) && !mesh_ftz_is_nan(inf) &&
51 !mesh_ftz_is_nan(SUB));
52 CHECK(mesh_ftz_ne(nan, nan) && mesh_ftz_cmp(nan, 1.0f) == MESH_FTZ_UNORDERED);
53 CHECK(mesh_ftz_eq(-0.0f, 0.0f) && mesh_ftz_gt(1.0f, -SUB) && mesh_ftz_lt(-1.0f, 0.0f));
54}
55
56/* ---- golden digests (gold ftz, from the Rust at alpha-rust-mesh 00b526b) ---- */
57
58static const uint32_t SPECIALS[44] = {
59 0x00000000, 0x80000000, 0x00000001, 0x80000001, 0x007fffff, 0x807fffff, 0x00400000, 0x80400000,
60 0x00800000, 0x80800000, 0x00800001, 0x3f800000, 0xbf800000, 0x3f000000, 0x7f7fffff, 0xff7fffff,
61 0x7f800000, 0xff800000, 0x7fc00000, 0xffc00000, 0x7f800001, 0x7fa00000, 0xffbfffff, 0x7fffffff,
62 0x01000000, 0x0c000000, 0x33800000, 0x34000000, 0x4b800000, 0x5f000000, 0x1f800000, 0x20000000,
63 0x00000002, 0x00100000, 0x3fc00000, 0x40490fdb, 0xc0490fdb, 0x2edbe6ff, 0x0d800000, 0x8d800000,
64 0x00ffffff, 0x3f7fffff, 0x7effffff, 0x1fffffff,
65};
66
67static uint64_t fnv(uint64_t h, uint64_t w) { return (h ^ w) * 0x100000001b3ull; }
68
69static uint32_t mix(uint32_t x) { /* mesh-ref block.rs lowbias32 */
70 x ^= x >> 16;
71 x *= 0x7feb352du;
72 x ^= x >> 15;
73 x *= 0x846ca68bu;
74 return x ^ (x >> 16);
75}
76
77static uint32_t gen(uint32_t m, uint32_t i) {
78 uint32_t h = mix((i * 0x9e3779b9u) ^ (m * 0x85ebca6bu));
79 uint32_t h2 = mix(h ^ 0x27d4eb2fu);
80 switch (m) {
81 case 0: return h;
82 case 1: return (h & 0x80000000u) | ((h2 % 41) << 23) | (h & 0x007fffffu);
83 case 2: return (h & 0x80000000u) | ((50 + h2 % 31) << 23) | (h & 0x007fffffu);
84 default:
85 if (i % 2 == 0) return (h & 0x80000000u) | ((1 + h2 % 40) << 23) | (h & 0x007fffffu);
86 return (h & 0x80000000u) | ((120 + h2 % 41) << 23) | (h & 0x007fffffu);
87 }
88}
89
90static uint64_t f64_bits(double d) {
91 union {
92 double d;
93 uint64_t u;
94 } p;
95 p.d = d;
96 return p.u;
97}
98
99static double f64_from(uint64_t u) {
100 union {
101 double d;
102 uint64_t u;
103 } p;
104 p.u = u;
105 return p.d;
106}
107
108static uint64_t ops(uint64_t h, float a, float b) {
109 int32_t c = mesh_ftz_cmp(a, b);
110 uint64_t cmp = c == MESH_FTZ_LESS ? 0 : c == MESH_FTZ_EQUAL ? 1 : c == MESH_FTZ_GREATER ? 2 : 3;
111 uint64_t v[9] = {bits(mesh_ftz_add(a, b)), bits(mesh_ftz_sub(a, b)), bits(mesh_ftz_mul(a, b)),
112 bits(mesh_ftz_div(a, b)), bits(mesh_ftz_neg(a)), bits(mesh_ftz_abs(a)),
113 f64_bits(mesh_ftz_to_f64(a)), cmp, mesh_ftz_is_nan(a)};
114 for (int i = 0; i < 9; i++) h = fnv(h, v[i]);
115 return h;
116}
117
118static void golden(const char *what, uint64_t got, uint64_t want, uint64_t flushes, uint64_t want_flushes) {
119 if (got != want || flushes != want_flushes) {
120 fprintf(stderr, "%s: digest %#018llx flushes %llu, want %#018llx flushes %llu\n", what,
121 (unsigned long long)got, (unsigned long long)flushes, (unsigned long long)want,
122 (unsigned long long)want_flushes);
123 failures++;
124 }
125}
126
127static void rust_golden(void) {
128 uint64_t f0 = mesh_ftz_flushes(), h = 0xcbf29ce484222325ull;
129 for (int i = 0; i < 44; i++)
130 for (int j = 0; j < 44; j++) h = ops(h, f(SPECIALS[i]), f(SPECIALS[j]));
131 golden("specials", h, 0xbff860139a4dfbe2ull, mesh_ftz_flushes() - f0, 4664);
132
133 static const uint64_t want[4] = {0x1a90a77d71d05624ull, 0xc547bacc65330903ull, 0xe7ef00233428a479ull,
134 0x7cfe6dcde1a58f4full};
135 static const uint64_t want_flushes[4] = {134486, 319309, 396123, 337035};
136 for (uint32_t m = 0; m < 4; m++) {
137 f0 = mesh_ftz_flushes();
138 h = 0xcbf29ce484222325ull;
139 for (uint32_t i = 0; i < 1000000; i++) h = ops(h, f(gen(m, 2 * i)), f(gen(m, 2 * i + 1)));
140 char what[16];
141 snprintf(what, sizeof what, "sweep %u", m);
142 golden(what, h, want[m], mesh_ftz_flushes() - f0, want_flushes[m]);
143 }
144
145 f0 = mesh_ftz_flushes();
146 h = 0xcbf29ce484222325ull;
147 for (uint32_t i = 0; i < 1000000; i++) {
148 uint32_t hi = mix(i ^ 0x5bd1e995u), lo = mix(hi ^ i);
149 uint64_t e = 1023 - 160 + hi % 200;
150 uint64_t b = (((uint64_t)hi & 0x80000000u) << 32) | (e << 52) | (((uint64_t)hi & 0xfffff) << 32) | lo;
151 h = fnv(h, bits(mesh_ftz_from_f64(f64_from(b))));
152 }
153 const double ds[12] = {1e-40, -1e-40, 1e-30, 1e300, -1e300, f64_from(0x7ff8000000000000ull),
154 f64_from(0xfff8000000000000ull), f64_from(0x7ff0000000000001ull),
155 1.1754942106924411e-38, 1.1754943508222875e-38, 0.0, -0.0};
156 for (int i = 0; i < 12; i++) h = fnv(h, bits(mesh_ftz_from_f64(ds[i])));
157 golden("from_f64", h, 0xfafb102caf21f6dcull, mesh_ftz_flushes() - f0, 120410);
158}
159
160int main(void) {
161 rust_inputs_and_results_flush_sign_preserving();
162 rust_compares_and_converts_flush();
163 rust_golden();
164 if (failures) fprintf(stderr, "%d failures\n", failures);
165 return failures != 0;
166}