From c5d9514cd9d92ce1845de254e4ef92e740f8c645 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 6 Aug 2022 18:54:19 +0200 Subject: [PATCH] Interpreter: Fix rounding edge case in frsp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the Dolphin bug mentioned in https://github.com/dolphin-emu/hwtests/issues/45. Because this doesn't fix any observed behavior in games (no, 1080° Avalanche isn't affected), I haven't implemented this in the JITs, so as to not cause unnecessary performance degradations. --- .../PowerPC/Interpreter/Interpreter_FPUtils.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h index 4e0601b897..94ebc65a83 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h @@ -52,6 +52,25 @@ inline void SetFPException(UReg_FPSCR* fpscr, u32 mask) inline float ForceSingle(const UReg_FPSCR& fpscr, double value) { + if (fpscr.NI) + { + // Emulate a rounding quirk. If the conversion result before rounding is a subnormal single, + // it's always flushed to zero, even if rounding would have caused it to become normal. + + constexpr u64 smallest_normal_single = 0x3810000000000000; + const u64 value_without_sign = + Common::BitCast(value) & (Common::DOUBLE_EXP | Common::DOUBLE_FRAC); + + if (value_without_sign < smallest_normal_single) + { + const u64 flushed_double = Common::BitCast(value) & Common::DOUBLE_SIGN; + const u32 flushed_single = static_cast(flushed_double >> 32); + return Common::BitCast(flushed_single); + } + } + + // Emulate standard conversion to single precision. + float x = static_cast(value); if (!cpu_info.bFlushToZero && fpscr.NI) {