Expand description
Soft-float arithmetic with exact IEEE-754 exception flags (T-13-003).
§Why this exists
Rust’s f32/f64 operators give a correctly-rounded result and discard
everything else: there is no way to ask whether the operation was inexact,
underflowed, or what it would have produced under a directed rounding mode.
The VR4300 reports all of that through FCSR, so an emulator built on the
native operators can be bit-exact on values and still wrong on every flag —
which is precisely where accuracy ledger C-11 left the FPU.
§Why the cheap version does not work
The tempting shortcut is to compute in f64 and compare: if the f64
result differs from the widened f32 result, call it inexact. That is
right in the normal range and wrong where it matters:
- For
MUL.Sit happens to hold — the exact product of two 24-bit significands needs at most 48 bits, andf64carries 53. - For
ADD.Sit does not. The exact sum of2^127and2^-149spans ~277 significand bits, so thef64sum is itself rounded and the comparison silently becomes a guess. - For any
.Doperation there is no wider type to compute in at all.
An earlier attempt along those lines was implemented and reverted (C-10). A flag that is right in the common case and wrong in the range the oracle deliberately probes is worse than no flag, because it makes every later result stop being evidence.
§How this works instead
One code path for both formats, parameterized by Format. Values are
unpacked to (sign, significand, exponent) with the significand as a plain
integer — value = sig × 2^exp — computed at a widened scale in u128, and
rounded once at the end by a single internal rounding step, which is the
only place any flag is
produced. Bits that fall off the bottom are never simply dropped: they are
folded into a sticky bit, which is what makes inexact exact rather than
approximate.
There is no unsafe, no allocation and no std; the widest type used is
u128, which core provides everywhere this crate builds.
§What is deliberately NOT modeled here
The VR4300 does not produce subnormal results: it raises the unmaskable
unimplemented-operation cause for subnormal operands and results (unless
FCSR.FS is set, which flushes instead). This module implements the IEEE
behavior and produces the subnormal, because that separation is what lets
it be checked against an independent oracle — every f32/f64 operation in
Rust. Layering the VR4300’s refusal on top is a separate change; doing both
at once would leave the arithmetic with nothing to be tested against.
Structs§
- Format
- The parameters of an IEEE-754 binary interchange format.
- Rounded
- A computed result: the encoding plus what producing it raised.
Constants§
Functions§
- add
a + b.- convert
- Convert between formats —
CVT.S.Dnarrowing,CVT.D.Swidening. - div
a ÷ b.- from_
int - Convert a two’s-complement integer to
f, honoringmode. - mul
a × b.- sqrt
SQRT.fmt— correctly rounded, with exact flags.- sub
a - b— addition with the subtrahend’s sign flipped, which is exact and is how the hardware does it too.