From 6ba8f83c30c46a4b7fb27b1556e744546a2e83d6 Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Sat, 2 May 2026 12:29:07 +0200 Subject: [PATCH] fix(cpu): PPCBUG-184 fresx pre-quantize input to f32 (canary parity) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- crates/xenia-cpu/src/interpreter.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/xenia-cpu/src/interpreter.rs b/crates/xenia-cpu/src/interpreter.rs index e4d88cd..051aa4f 100644 --- a/crates/xenia-cpu/src/interpreter.rs +++ b/crates/xenia-cpu/src/interpreter.rs @@ -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);