fix(cpu): PPCBUG-184 fresx pre-quantize input to f32 (canary parity)

Phase 5 batch 5 (5e): minimal-viable fix for the estimate-precision
family. Hardware Xenon `fres` produces a ~12-bit LUT estimate; xenia
and canary both produce a fully IEEE single reciprocal, but canary
pre-quantizes the f64 input to f32 to at least match the input
precision. Now matches canary.

PPCBUG-428..431 (vrefp/vrsqrtefp/vexptefp/vlogefp) already operate on
f32 inputs naturally (no f64 → f32 quantization step needed); the
estimate-precision deviation is purely the output side. Newton-Raphson
convergence is unaffected. Documented in audit-findings.md as
LOW-impact full-fix-requires-LUT.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-05-02 12:29:07 +02:00
parent 538fa5ab74
commit 6ba8f83c30

View File

@@ -2777,12 +2777,18 @@ fn execute(ctx: &mut PpcContext, mem: &dyn MemoryAccess, instr: &DecodedInstr) -
ctx.pc += 4;
}
PpcOpcode::fresx => {
// Single-precision reciprocal estimate: frD = 1.0 / frB
let b = ctx.fpr[instr.rb()];
// Single-precision reciprocal estimate: frD = 1.0 / frB.
// PPCBUG-184: pre-quantize input to f32 to match canary's
// `f.Recip(f.Convert(frB, FLOAT32_TYPE))` behavior. Hardware
// produces a ~12-bit LUT estimate; both emulators produce a
// fully-IEEE single reciprocal, but the f32 quantization at
// least makes the input precision match.
let b_full = ctx.fpr[instr.rb()];
let b = b_full as f32 as f64;
if b == 0.0 {
fpscr::set_exception(ctx, fpscr::ZX);
}
if fpscr::is_snan(b) {
if fpscr::is_snan(b_full) {
fpscr::set_exception(ctx, fpscr::VXSNAN);
}
let result = to_single(ctx, 1.0 / b);