8 type impl_prog = struct
20 /* Flt is ``whatever precision we're testing''. */
36 type exactness = union
41 /* (name, number-of-flt-args, constant-extra-bytes) */
42 var available_fns : fn_desc[:] = [][:]
46 const main = {args : byte[:][:]
48 [.name = "id", .inputs = [`Flt][:], .outputs = [`Flt][:]],
49 [.name = "atan", .inputs = [`Flt][:], .outputs = [`Flt][:]],
50 [.name = "atan2", .inputs = [`Flt, `Flt][:], .outputs = [`Flt][:]],
51 [.name = "ceil", .inputs = [`Flt][:], .outputs = [`Flt][:]],
52 [.name = "cos", .inputs = [`Flt][:], .outputs = [`Flt][:]],
53 [.name = "cot", .inputs = [`Flt][:], .outputs = [`Flt][:]],
54 [.name = "exp", .inputs = [`Flt][:], .outputs = [`Flt][:]],
55 [.name = "expm1", .inputs = [`Flt][:], .outputs = [`Flt][:]],
56 [.name = "floor", .inputs = [`Flt][:], .outputs = [`Flt][:]],
57 [.name = "fma", .inputs = [`Flt, `Flt, `Flt][:], .outputs = [`Flt][:]],
58 [.name = "log", .inputs = [`Flt][:], .outputs = [`Flt][:]],
59 [.name = "log1p", .inputs = [`Flt][:], .outputs = [`Flt][:]],
60 [.name = "powr", .inputs = [`Flt, `Flt][:], .outputs = [`Flt][:]],
61 [.name = "sin", .inputs = [`Flt][:], .outputs = [`Flt][:]],
62 [.name = "sincos",.inputs = [`Flt][:], .outputs = [`Flt, `Flt][:]],
63 [.name = "sqrt", .inputs = [`Flt][:], .outputs = [`Flt][:]],
64 [.name = "tan", .inputs = [`Flt][:], .outputs = [`Flt][:]],
65 [.name = "trunc", .inputs = [`Flt][:], .outputs = [`Flt][:]],
70 .handler = (nop : byte#),
71 .flags = sys.Saresethand,
73 sys.sigaction(sys.Sigpipe, &sa, &old)
75 var fn : fn_desc = available_fns[0]
76 var next_bits_fn : (b : byte[:], n : std.size -> bool) = next_rand
78 var precision : flt_prec = `Single
79 var exactness : exactness = `Exact
80 var impls : impl_prog#[:]
81 rng = std.mksrng((std.now() : uint32))
83 (precision, exactness, fn, next_bits_fn) = read_args(args)
85 var input_sz = args_width(fn.inputs, precision)
86 var num_inputs = (1 << 18)
87 var buf_sz = num_inputs * input_sz
89 impls = start_impls([prec_arg(precision), "-f", fn.name, "-n", std.fmt("{}", num_inputs)][:])
91 io_loop(impls, precision, exactness, num_inputs, fn, next_bits_fn)
109 const args_width = {ts : fp_type[:], p : flt_prec
114 | `Flt: w += prec_width(p)
121 const read_args = {args : byte[:][:]
122 var exactness : exactness = `Exact
123 var precision : flt_prec = `Single
124 var fn_name : byte[:] = "UNSPECIFIED"
125 var fn : fn_desc = available_fns[0]
126 var next_bits_fn : (b : byte[:], n : std.size -> bool) = next_rand
128 var cmd = std.optparse(args, &[
131 [.opt = 's', .desc = "use single precision (default)"],
132 [.opt = 'd', .desc = "use double precision"],
133 [.opt = 'l', .desc = "list available functions"],
134 [.opt = 'f', .arg = "f", .desc = "test function ‘f’"],
135 [.opt = 'r', .arg = "s", .desc = "choose inputs randomly, with seed ‘s’"],
136 [.opt = 'e', .desc = "exhaust input space"],
137 [.opt = 'i', .arg = "i", .desc = "allow inexact matches (by ‘i’, bitwise)"],
148 match std.intparse(arg)
151 exactness = `Inexact (n : uint)
153 std.put("unacceptable inexactness “{}”\n", arg)
156 std.put("cannot parse inexactness “{}”\n", arg)
162 | ('f', arg): fn_name = arg
164 next_bits_fn = next_rand
165 match std.intparse(arg)
169 std.put("cannot parse seed “{}”\n", arg)
172 | ('e', _): next_bits_fn = next_exhaust
173 | _: std.die("impossible\n")
177 var good_fn : bool = false
178 for f : available_fns
179 if std.eq(f.name, fn_name)
187 std.put("unknown function “{}”\n", fn_name)
191 -> (precision, exactness, fn, next_bits_fn)
194 const io_loop = {impls : impl_prog#[:], p : flt_prec, x : exactness, num_inputs : std.size, fn : fn_desc, next_bits_fn : (b : byte[:], n : std.size -> bool)
195 var input_sz = args_width(fn.inputs, p)
196 var output_sz = args_width(fn.outputs, p)
197 var draw_line : bool = false
199 var bits : byte[:] = std.slalloc(num_inputs * input_sz)
200 var last_bits : byte[:] = std.slalloc(num_inputs * input_sz)
202 std.slfill(last_bits, 0)
204 i.output_bits = std.slalloc(num_inputs * output_sz)
205 std.slfill(i.output_bits, 0)
208 /* Now, loop perhaps infinitely with the comparisons */
214 match std.writeall(i.stdin, bits)
217 std.put("CRASH: {w=20} [{w=6}] failed to receive data\n", i.name, i.pid)
224 for var j = 0; j < impls.len; ++j
228 impls[j] = impls[impls.len - 1]
229 std.slgrow(&impls, impls.len - 1)
232 /* Gather consensus on last time's answers */
233 consensus(last_bits, num_inputs, fn, p, x, impls)
236 std.put("\x1b[1G\x1b[0K")
242 std.slcp(last_bits, bits)
244 /* Receive new answers */
250 match std.readall(i.stdout, i.output_bits)
253 std.put("CRASH: {w=20} [{w=6}] failed to send data\n", i.name, i.pid)
261 std.put("Less than 2 implementations left. Consensus impossible.\n")
262 std.put("----------\n")
267 std.put("----------\n")
272 if next_bits_fn(bits, num_inputs)
277 const next_rand = { b : byte[:], n : std.size
278 std.rngrandbytes(rng, b)
282 const next_exhaust = { b : byte[:], n : std.size
283 var one_arg : byte[:] = std.slalloc(b.len / n)
284 std.slcp(one_arg, b[b.len - one_arg.len:])
285 var finished : bool = false
287 /* n is the number of total argument groups */
288 for var i = 0; i < n; ++i
289 /* Increment this particular argument */
290 var j = one_arg.len - 1
299 finished = finished || j < 0
300 var z = one_arg.len * i
301 std.slcp(b[z:z + one_arg.len], one_arg)
307 const list_functions = {
308 std.put("Available functions:\n")
309 std.put("--------------------\n")
310 for f : available_fns
315 const start_impls = {opts : byte[:][:]
316 var cmd : byte[:][:] = std.slalloc(opts.len + 1)
317 var nice_name : byte[:] = [][:]
318 var started_impls : impl_prog#[:] = [][:]
319 var survived_impls : impl_prog#[:] = [][:]
321 for var j = 0; j < opts.len; ++j
325 /* Start everything */
326 for f : fileutil.bywalk(".")
327 match std.strrfind(f, "/")
328 | `std.Some j: nice_name = std.sldup(f[j+1:])
329 | `std.None: nice_name = std.sldup(f)
332 if nice_name.len < 5 || !std.eq(nice_name[:5], "impl-") || \
333 std.eq(nice_name[nice_name.len - 2:nice_name.len - 1], ".")
334 std.slfree(nice_name)
340 | `std.Ok (p, fi, fo) :
341 std.slpush(&started_impls, std.mk([
348 | `std.Err e: std.slfree(nice_name)
352 /* Give them a bit of time to die */
355 /* Reap the zombies */
358 var WNOHANG = 1 /* HACK */
359 while ((z = sys.waitpid(-1, &l, WNOHANG)) > 0)
360 match sys.waitstatus(l)
364 | `sys.Waitstop _: continue
366 for i : started_impls
367 if i.pid == (z : std.pid)
374 for i : started_impls
379 std.slpush(&survived_impls, i)
382 match survived_impls.len
384 std.put("No implementations found. Try running from fpmath-consensus root dir.\n")
387 std.put("Only one implementation found. Comparisons will be impossible.\n")
392 std.put("Executing:\n")
393 std.put("----------\n")
394 for i : survived_impls
395 std.put(" [{w=6}] {w=20}", i.pid, i.name)
401 std.put("----------\n")
403 std.slfree(started_impls)
408 const consensus = { input : byte[:], num_inputs : std.size, fn : fn_desc, p : flt_prec, x : exactness, impls : impl_prog#[:]
409 var all_agree : bool = true
410 var flt_sz : std.size = prec_width(p)
411 var inputs_sz = args_width(fn.inputs, p)
412 var outputs_sz = args_width(fn.outputs, p)
414 for var z = 0; z < num_inputs; ++z
417 /* The input is possibly multiple entries */
418 var i_start = z * inputs_sz
419 var i_end = (z + 1) * inputs_sz
421 /* The output might also be strange */
422 var a_start = z * outputs_sz
423 var a_end = (z + 1) * outputs_sz
425 for var j = 0; j + 1 < impls.len; ++j
428 if std.sleq(impls[j].output_bits[a_start:a_end], impls[j+1].output_bits[a_start:a_end])
432 /* The memory patterns don't agree. But perhaps this is due to NaN? */
433 if detailed_eq(impls[j].output_bits[a_start:a_end], impls[j+1].output_bits[a_start:a_end], fn.outputs, p, 0)
437 var good : bool = true
439 for var k = j + 1; k < impls.len; ++k
440 good = good && detailed_eq(impls[j].output_bits[a_start:a_end], impls[k].output_bits[a_start:a_end], fn.outputs, p, i)
457 std.put("For input: ")
458 extract_and_describe(input[i_start:i_end], fn.inputs, p)
461 std.put(" [{w=6}] {w=20}: ", i.pid, i.name)
462 extract_and_describe(i.output_bits[a_start:a_end], fn.outputs, p)
464 std.put("----------\n")
468 const extract_and_describe = {bits : byte[:], ts : fp_type[:], p : flt_prec
469 var w : std.size = prec_width(p)
484 var u = std.getle32(bits[:w])
485 std.put("0x{w=8,p=0,x} ({})\n", u, std.flt32frombits(u))
487 var u = std.getle64(bits[:w])
488 std.put("0x{w=16,p=0,x} ({})\n", u, std.flt64frombits(u))
496 const detailed_eq = {a : byte[:], b : byte[:], ts : fp_type[:], p : flt_prec, i : uint
497 var w : std.size = prec_width(p)
504 var u1 = std.getle32(a[:w])
505 var u2 = std.getle32(b[:w])
506 var f1 = std.flt32frombits(u1)
507 var f2 = std.flt32frombits(u2)
508 if !((f1 == f2) || (std.isnan(f1) && std.isnan(f2)))
509 if i == 0 || ((u1 - u2 > (i : uint32)) && (u2 - u1 > (i : uint32)))
514 var u1 = std.getle64(a[:w])
515 var u2 = std.getle64(b[:w])
516 var f1 = std.flt64frombits(u1)
517 var f2 = std.flt64frombits(u2)
518 if !((f1 == f2) || (std.isnan(f1) && std.isnan(f2)))
519 if i == 0 || ((u1 - u2 > (i : uint64)) && (u2 - u1 > (i : uint64)))