Skip to main content

Module softfloat

Module softfloat 

Source
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.S it happens to hold — the exact product of two 24-bit significands needs at most 48 bits, and f64 carries 53.
  • For ADD.S it does not. The exact sum of 2^127 and 2^-149 spans ~277 significand bits, so the f64 sum is itself rounded and the comparison silently becomes a guess.
  • For any .D operation 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§

F32
Single precision.
F64
Double precision.

Functions§

add
a + b.
convert
Convert between formats — CVT.S.D narrowing, CVT.D.S widening.
div
a ÷ b.
from_int
Convert a two’s-complement integer to f, honoring mode.
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.