libcft, compiled to WebAssembly, checked in your browser

The claim under test: the same call, with the same inputs, returns the same bits - on every backend, on every machine, on every day (host/include/cft.h). This page loads the library's software backend as wasm and replays the project's published conformance vectors through it, in this tab, with no server and no network. The backend's arithmetic is integer-only - uint32 limbs, uint64 intermediates - and WebAssembly integer semantics are fully specified, so what runs here is not a lookalike of libcft: it is libcft, and it must produce the library's exact bits or fail below.

1 · the library that loaded

loading the wasm module…

2 · embedded sample replay

This page carries a deterministic sample of the published vector sets and replays it automatically on load, through cft_conformance() itself - the library's own parser, per-element pass (exact flags per case), array pass (batching), and its refusal to let a run that checked nothing read as a pass. Not a JavaScript reimplementation of the replay: the same C code path every other backend is judged by.

Sampling rule (deterministic, restated in build.sh): regenerate the published sets with ; from each of the 20 opcode sets (4 formats × 5 rounding attributes, 11,800 cases each) take every 59th line - 0-based lines 0, 59, 118, … - then add the set's first line for any opcode name the stride missed, so every opcode class is present per set by construction, the seed opcodes recip_seed/rsqrt_seed and the unassigned reserved15/30/255 included. (That list has shed a member three times - 24 became sum, 26 recip_seed, and 28 sumsq at ABI 0.6 - which is the hazard a published reserved case carries and the reason the replayer refuses a set whose reserved opcode has since been assigned.) The other four families - transcendental, augmented, reduction, character - are not embedded; drop them in section 3, where they are replayed whole.

waiting for the wasm module…

3 · full replay: drop the published sets

The embedded sample is a sample. For the full claim, generate the complete sets in a checkout of this repo - make vectors, which writes 1,068,915 cases over 168 sets to vectors/out/: 240,000 over twenty opcode sets, 533,265 over twenty transcendental sets (all thirty-nine functions since ABI 0.6, table 9.1 complete), 89,616 over the four augmented sets of clause 9.5, 8,960 over twenty reduction sets, 13,816 over twenty character sets, and since ABI 0.7 9,728 over the four magnitude sets of clause 9.6 and 176,250 over the eighty formatOf sets of clause 5.4.1 - one per ordered pair of formats per attribute - and drop any or all of the .jsonl files here. Same code path as section 2, whole files this time.

drop vectors/out/*.jsonl here — or click to pick files

Only the generator's fixed names are accepted - all 168 of them: fp32.jsonl … fp256-rmm.jsonl, fp32-transcend.jsonl … fp256-transcend-rmm.jsonl, fp32-augmented.jsonl … fp256-augmented.jsonl (one per format, because 754-2019 9.5 fixes the rounding and there is no attribute to sweep), fp32-reduce.jsonl … fp256-reduce-rmm.jsonl, fp32-character.jsonl … fp256-character-rmm.jsonl, fp32-minmaxmag.jsonl … fp256-minmaxmag.jsonl (one per format again, and for a sharper reason: 9.6's magnitude forms compare two magnitudes and then select an operand, so there is no rounding for an attribute to direct) and fp32-to-fp32-formatof.jsonl … fp256-to-fp256-formatof-rmm.jsonl (sixteen ordered pairs × five attributes; a case there carries a source-format operand and a destination-format result on the same line, and names both formats so the file cannot lie about which pairing it is) - cft_conformance enumerates sets by name rather than by scanning, and a file this page cannot map to a set is refused loudly rather than skipped quietly.

4 · compute

The same library, called forward: one element through cft_run(), or the composed cft_div() / cft_sqrt() sequence, or any of the thirty-nine transcendentals - phase 1's nine (cft_expcft_hypot, ABI 0.3), phase 2's eleven (cft_sinpicft_atan2pi, ABI 0.4), phase 3's nine (cft_sincft_atanh, ABI 0.5, sin/cos/tan in radians) and the ten that complete 754-2019 table 9.1 (cft_exp2m1cft_rootn, ABI 0.6) - operands and result as raw encodings. This is a working binary32/64/128/256 calculator, and every answer it gives is pinned by the replay above. The transcendentals are correctly rounded, which is why they can be in a determinism contract at all: the answer is defined by the mathematics, not by an algorithm, so every correct implementation agrees with this one bit for bit. The eleven are the ones whose argument reduction is exact - sinPi's is x mod 2, a mask on the encoding, and the inverses meet π only as a factor of the answer - while sin, cos and tan of a radian argument reduce against the library's own 270,336-bit 2/π instead.

ABI 0.6 also puts the other three packages here. Clause 5.12's character conversions read and write a sequence rather than an encoding - the panel grows a text field for one and a digit count for the other, and the two-call sizing protocol it runs to get an answer out is the C's own, not a shorter one invented for JavaScript. Clause 9.7's three payload operations signal nothing at all, so there is no flag line under them. Clause 9.5's augmented arithmetic and clause 9.4's scaled products each return a pair, shown on two lines: r and e, whose sum is exactly the operation; or pr and an int64 sf, whose scaleB is the product. The augmented three ignore the rounding select and grey it out, because 9.5 fixes their direction to roundTiesTowardZero, which is not one of clause 4.3's five attributes. sumsq, sumabs and the scaled products are reductions over a whole vector; this panel is a one-element calculator, so it runs them over the single operand, which is an answer the contract names rather than a special case invented for the page.

ABI 0.7 adds two more. Clause 9.6's four magnitude forms of minimum and maximum are here beside the four opcodes they defer to, and like the augmented three they grey the rounding select out - not because an attribute is fixed but because there is no rounding at all: each compares two sign-cleared encodings and then selects one operand, so the answer is an operand's own bits except where the base operation delivers a NaN. The row worth trying is equal magnitudes of opposite sign: maximumMagnitude(+3, -3) is the “otherwise” case and defers to maximum, so it is +3, and an implementation that quietly preferred the second operand would be wrong here and nowhere else. Clause 5.4.1's six formatOf operations take two formats, so the panel grows a second select: the operands are read in the source format, the result is delivered in the destination one, and there is exactly one rounding between them. Narrowing is not “compute there, convert here” - that rounds twice, and on operands this ladder can construct the composed answer is one ulp low with the same flags.

The status word under the result is 754-2019 7.1's: one sticky word on the device handle into which every call ORs what it signalled, which nothing in the library ever lowers. The flags line above it is the last call's; this one is every call since the page loaded or since you last cleared it, which is the distinction 7.1 exists to draw. Sections 2 and 3 are calls too, so the word is already carrying the replay's exceptions before you compute anything — press lower all flags first if you want to watch one operation on its own. It never influences a result - libcft never reads it back to decide anything - so clearing it or not changes no answer anywhere.

754-2019 7.1 status word, sticky on this device since the page loaded — the union of every call above, lowered only at your request:

(nothing raised)