t81-foundation

Native floating-point conversion audit

The PR #551 P1 finding is real. Production conversion remains unresolved. This corrective increment quarantines unsupported conversion observations from canonical provenance, preserves the failures, and adds an independent exact reference. It does not change T81Float, its public API or any numeric guarantee. The integer workload, policy gates, trace verification and tamper checks remain.

Reproduce

No third-party Python libraries, model files or full T81 build are needed for the audit (Python >=3.10, C++23 compiler; IEEE binary64 double required):

c++ -std=c++23 -O0 -DNDEBUG -DT81_DETERMINISTIC -I include \
  experiments/float_conversion_audit/probe.cpp -o /tmp/t81-float-o0
c++ -std=c++23 -O3 -DNDEBUG -DT81_DETERMINISTIC -I include \
  experiments/float_conversion_audit/probe.cpp -o /tmp/t81-float-o3
python3 experiments/float_conversion_audit/test_oracle.py
python3 experiments/float_conversion_audit/audit.py \
  --probe /tmp/t81-float-o3 --unoptimized-probe /tmp/t81-float-o0 \
  --output /tmp/t81-float-native.json

Run the unchanged bounded workload and adversarial suite using the new evidence-v2 fixture. The native audit workflow executes these commands on Linux x86-64/GCC 14 and macOS ARM64/Apple Clang, then builds only the provenance executable and compares all eight admitted records byte for byte. Artifacts are uploaded even on failure. A green audit means the reference, exclusion and integer checks passed; the report still labels numerical conversion UNRESOLVED, never a numerical-conformance success.

Reference profile and contract conflict

oracle.py starts with the exact 64-bit input encoding. It decomposes exponent and significand into a rational with Python integers; it never calls host float, log, pow, rounding or T81 code. Finite encoding values are interpreted as sign * integer_mantissa * 3**(exponent - (M-1)), matching the implementation’s field equation, not its conversion algorithm. For <27,9>:

spec/t81-data-types.md §2.3 requires explicit deterministic errors for invalid states and a Result[T,E] for precision loss, yet the current noexcept from_double(double) -> T81Float provides neither. It emits NaE/Inf sentinels and silently rounds. The spec also describes base-81/BigInt components, while this class implements bounded balanced-trit fields. Normalization comments explicitly say discarded trits are not properly rounded. There is no unambiguous, complete existing rounding/range/error contract to patch into this API without a numerical contract decision. The exact oracle exposes that gap; its diagnostic nearest candidate is not authorization to change public semantics.

Two independent tests check the oracle: exact binary64 decomposition and brute force enumeration of all finite encodings of a small <4,4> format. Coverage includes binade boundaries, ties, negative symmetry, special inputs, invalid encodings, destination range errors and exact/inexact distinctions. Matching a nearest candidate is distinct from satisfying the strict lossless contract.

Findings and before/after evidence

The production header is byte-for-byte unchanged from merged PR #551. Production outputs before and after this corrective PR are identical on each platform. The before/after change is the provenance contract: version 1 included unsupported conversions; version 2 excludes them and rejects v1 explicitly. Original v1 fixture.json and observed-arm64.json remain preserved under experiments/deterministic_provenance/; active verification uses fixture-v2.json. Every new provenance run retains the actual conversion strings in an explicit UNSUPPORTED_NONCANONICAL diagnostics section, outside record hashes.

The root causes are distinguishable:

  1. EffectiveM is size_t, so EffectiveM - 1 - exp_unb performs unsigned subtraction. At maximum finite binary64, exp_unb=647; the scale becomes 18446744073709550995 instead of -621. The subsequent power is nonfinite and the legacy path returns zero. This is not legitimate destination overflow.
  2. For minimum binary64 values, the scale is positive: 703 for denorm_min, 670 for minimum-normal on the tested ARM64 compiler. powl(3,703) and powl(3,670) overflow a binary64 long double before multiplication by the tiny input. Linux x86-64 extended long double can represent them. The mathematical scaled mantissa is finite in both cases. Fixing signedness alone does not remove this intermediate-range dependency.
  3. log/floor select exponent boundaries and llround performs host-precision half-away rounding before balanced-trit normalization discards digits. Even at 0.5, with finite intermediates, the production encoding lies farther from the exact input than the oracle’s nearest candidate. A powl workaround alone is not a numerical correction. Host log/rounding boundary behavior remains another dependency, not proven harmless by the extreme-value reproducer.
  4. On the tested native ARM64 toolchain, O0/O3 produce identical canonical strings for the corpus. Compiler optimization is not the observed cause there; this does not exclude all toolchain effects or floating-point modes.

Exact outputs, source/binary64 identifiers, independent candidates, compiler and long-double properties are in the native JSON evidence. represents_exact_input, matches_nearest_candidate and lossless_contract_satisfied are separate fields. The report does not count matching incorrect platform outputs as numerical success.

Decision and remaining limits

Use the user-authorized fallback: preserve numerical failures and exclude every unsupported conversion probe from canonical provenance. No production fix is claimed. No platform-specific expected values, weakened output/trace/policy checks, hash-only correctness test or new workload is introduced. Version 2 strictly rejects any reintroduced floating-point record fields, even when rehashed.

Open PR #536 remains unmerged at 3e52b6b5247b578e5f6a861a1b9a971b6e93c7cd. Its inspected patches do not touch T81Float.hpp::from_double; integer helper, BigInt-invariant and VM flag changes neither resolve these defects nor block this quarantine. No files from that PR are overwritten or incorporated.

The next numerical change requires an approved checked-conversion contract, integer/rational conversion with explicit rounding and range errors, and an audit of normalization and the inverse to_double path (also host powl based). Until then floating-point cross-platform identity and correctness are not established. The integer provenance milestone can advance only within its declared boundary when the native comparison passes. WASM and Windows remain NOT TESTED.

Local evidence (native macOS ARM64)

Measured source commit 273a49bc4a4e6cf3bb63b138ef28e03232e1d813: conversion observations and 240-run provenance recheck. Apple Clang 21.0.0; long double has 53 precision bits and maximum binary exponent

  1. Optimizations O0 and O3 produce identical canonical outputs on this corpus.
Exact binary64 input Production before = after Independent nearest candidate Checked reference outcome
0000000000000001 (denorm_min) +0E0 ++----00++++-0--00-+++0++000E-677 inexact
0010000000000000 (min) +0E0 +++-0+-+000+-+++-0+0++0+-0-0E-645 inexact
7fefffffffffffff (max) +0E0 ++0+-+-00+--+-0+--+0+---000+E646 inexact
3fe0000000000000 (0.5) ++--------------------------E0 ++++++++++++++++++++++++++++E-1 inexact
+/-0 +0E0 +0E0 exact
+/-infinity +Inf / -Inf no finite candidate invalid
NaN payloads NaE no finite candidate invalid

All seven targeted CTest suites passed, including eight provenance and five reference tests. All 240 integer executions verified with identical canonical v2 bytes per case; actual stores, policy denials and adversarial rehashed records were checked. The production header is unchanged. The normative/API conflict remains; this table records numerical failures, not a production fix.

Native CI reproduction

The first native run reproduced the discrepancy with GCC 14.2.0 on Linux x86-64 (64-bit long double precision, maximum binary exponent 16384) and Apple Clang 21.0.0 on macOS ARM64 (53 bits, exponent 1024). Native CI observations preserve all 24 encodings and their exact reference expectations. Neither platform changed canonical outputs between O0 and O3 for this corpus.

Input Linux production before = after macOS production before = after
denorm_min ++----00++++-0--00-+++0++000E-677 +0E0
min-normal +++-0+-+000+-+++-0+0++0+-0-0E-645 +0E0
max finite +0E0 +0E0
0.5 ++--------------------------E0 ++--------------------------E0
+/-0 +0E0 +0E0
+/-infinity +Inf / -Inf +Inf / -Inf
NaN payloads NaE NaE

Linux’s two tiny results match the nearest diagnostic candidates, while macOS’s zeros do not. Both platforms’ max/half results fail even nearest-value checking. The inexact and invalid statuses remain distinct from numerical success.

The first CI jobs passed all five reference tests and eight provenance tests per platform, plus 24 repeated provenance executions each. They then failed uploading raw CanonFS filenames containing <, which GitHub artifacts reject. The workflow now uploads tar bundles preserving every store filename and payload; no evidence is discarded. The initial overall run is recorded as failed, not a passing cross-platform comparison. A subsequent native comparison is required before the integer-only identity conclusion can advance.

The subsequent native run passed on source head 8394ac458bbfdcc13843705685f6c0ad3c278f97: both native jobs and the byte-for-byte comparison of all eight admitted integer records succeeded. Format and AI experiment checks passed too. CI validation records the source identity, artifact digests and explicit scope. Bounded integer provenance can advance for this native pair/corpus; floating-point provenance cannot. Conversion production code is unchanged and its failures remain excluded. This conclusion is based on actual native artifacts, not matching pass/fail statuses or hashes alone.