### The integer decode paths hold no floating point in their source, and read no JSON with a float parser

The integer square root of the inverse GDN, the splat codec's ranges and JSON form, and `int_entropy`'s scale
conversions no longer use floating point; the decode reads a parameter set's JSON with the strict reader, which
converts no number to a float; and a test of the sources keeps the integer decode paths, and the operator set they
run their networks through, free of floating point and of a float parser. No payload, corpus or decoded output
changes: the integer corpora regenerate byte for byte, on arm64 macOS and x86_64 Linux. The test reads source, not a
compiled decode.

- **The inverse GDN's root.** The narrow kernel sets seeded each integer square root from the IEEE-754 square root
  (`(n as f64).sqrt()`), and the AVX-512 kernel took eight at a time with a vector square root; the integer
  correction after each decided the result. The seed is now a fixed-point reciprocal square root (a table on the
  norm's top bits and two Newton steps, formed exactly in `u64` and `u128`), within one of the root, and the
  AVX-512 roots are taken digit by digit in integer lanes. The roots are the same integers. The test holds the root
  to its definition, an independent digit-by-digit root and `u64::isqrt` on the edges of `u64` (0, 1, every
  `k² − 1`, `k²` and `k² + 1` around the powers of two and across the range, `u64::MAX`), and an ignored test
  checks every boundary `k² − 1`, `k²` for `k` below `2^32`.
- **`WIS1` ranges** are read, checked and written as binary32 bits (`int_payload::wire::Binary32`): finite, and
  `lo ≤ hi` as IEEE 754 compares two finite values (`−0` equal to `+0`), in integer arithmetic. The rule
  (integer-payloads §6) is unchanged; a test holds it to the float comparison on every class of bit pattern.
- **The splat JSON form** (`splat.json`) is read from its text with the strict JSON reader: its integers as integer
  spellings, and each range by `int_splat::binary32_of` in exact integer arithmetic (integer-payloads §10, which
  now says how a number denotes a binary32 value). A number spelled as an integer is that integer, any other
  spelling denotes the binary64 value nearest it, and either must be a binary32 value; this is how the independent
  converter, `int_payload_verify.py`, reads a number. The reader before went through serde_json, whose float
  reading is correctly rounded only with its `float_roundtrip` feature (which `splat_int` turned on for the whole
  crate, and the byte-equality crate never had), and it disagreed with the independent converter in two places:
  it kept the sign of the integer `-0` (zero, so `+0`), and it rounded an integer past `u64` (refused unless it is
  a binary32 value). A range one digit from the binary64 tie between `1` and the next binary64 value now reads as
  binary64 rounds it on each side of the tie, with a test, which also holds two spellings one last digit apart
  (`4194304.0` and `4194304.5`, `8.388608e6` and `8.388609e6`) to the two adjacent values the payload's decode
  presents. The JSON decode refuses a range that is not a binary32
  value, as the converter always did, and the text is read strictly (a member named twice, or bytes after the
  value, refuse it). `splat_int` no longer turns on `float_roundtrip`; `splat_exact`, which reads an asset's
  `POSITION` bounds as JSON numbers, does.
- **`int_entropy::fixed_scale_edges` and `quantize_scale`** form `⌊x·2^frac⌋` from the binary32 bits in integers,
  the values the binary64 product and its floor gave, which a test checks at every exponent and every `frac`.
- **The parameter sets' JSON** (`entropy.json` of `wai.neural.int_hyper` and `wai.video.int_hyper`, integer-payloads
  §9, and a `WMB1` bundle's tables, §14) is read by the strict JSON reader, each integer from its spelling. The
  general JSON reader the crate depends on (serde_json) read it before: its number parser is binary64 code, which ran
  on a fraction, an exponent or an integer past `u64` before the integer reads refused them, so a decoder built into
  a module with no floating point could not hold it. §9 now says how the text is read, and two readings change: `-0`
  is zero, as §9's integer rule already gave (the general reader read it as a float, and the set was refused), and a
  member named twice, or a value more than 64 levels deep, refuses the set (`model_mismatch`), where the general
  reader kept the last of two members and read deeper values. The committed sets read as before, with tests of each
  reading through `decode_int` and through a `WMB1` bundle.
- **A test of the sources** (`float_free`) fails on a floating-point type, literal or vector intrinsic in any module
  on the integer decode paths, outside its tests and model-checker harnesses: the kernels, the operator-set bridge,
  the payloads, the integer codecs, the rANS coder and its bucketing, the strict JSON reader, and the other integer
  entropy decoders and the decodes built on them (JPEG AI's mANS decoder, `WIR1`, the NNC tensor's arithmetic decoder
  and its tensor decode, `dred-int/1` but its floating-point reference and measurement, and `wai.avatar.face`); in
  every file of the operator set (`wai-ops`); and on a call of the general reader's parser in those modules, but two
  that are not decodes: the direct `int_synth` decode kept for the differential tests, and the face verifier's read
  of an envelope's manifest. Every `int_*` file, and every file under `int_kernels/` and `int_payload/`, must be
  among those it reads, so a new one fails it until it is listed. The tools' JSON forms (`from_value`, the
  `*_from_json` converters) still take a `serde_json::Value`; they are converters, not decodes. Outside these paths,
  `exact_tonemap`'s ST 2094-50 reader still forms binary64 slopes for its measurement, beside the Q30 slopes its
  integer derivation uses.
- **Breaking:** `api:rust:wai::int_payload::IntDecoded::Attr`: its `ranges` are `(Binary32, Binary32)` pairs, the
  bits the payload carries, so the decode forms no float. A sink presenting a range takes `f32::from_bits(r.0)`.
  `Binary32` is re-exported as `int_payload::Binary32`.
- **Breaking:** `api:rust:wai::int_payload::wire::Wis1::ranges`, and so `wire::parse_wis1`'s result and
  `wire::write_wis1`'s argument: `(Binary32, Binary32)` bits, where they were `(f32, f32)`. Build one with
  `Binary32(x.to_bits())` and read it with `f32::from_bits(b.0)`.
- **Breaking:** `api:rust:wai::int_hyper_synth::CondEntropy::try_from_value`: replaced by `CondEntropy::read`, which
  takes the file's bytes and reads them as a set's decode does, so no float parser reads the tables first. Call it
  with `entropy.json`'s bytes in place of a parsed value.
- **Breaking:** `api:rust:wai::int_splat::SplatJson::ranges`: the binary32 values `lo` and `hi` denote, as
  `(Binary32, Binary32)`, where they were `(f64, f64)` as a float parser read them.
- **Breaking:** `api:rust:wai::int_splat::SplatJson::from_value`, `api:rust:wai::int_splat::decode` and
  `api:rust:wai::int_payload::wis1_from_json`: the JSON form is read from its text, so no float parser reads it
  first. Call `SplatJson::read`, `decode` and `wis1_from_json` with the file's bytes in place of a parsed value.
