diff options
| author | Mai <mathew1800@gmail.com> | 2022-08-09 18:17:21 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-08-09 18:17:21 -0400 |
| commit | c2dd58c3615dbf89552e5f2fa5b283fa6fc56fff (patch) | |
| tree | 6468a3df111f1be3697706b5c392a4c15d16f12c | |
| parent | e9e2c741a20da47191ba4585fdd5c9a1d5e32f5a (diff) | |
| parent | c5d9514cd9d92ce1845de254e4ef92e740f8c645 (diff) | |
Merge pull request #10959 from JosJuice/frsp-subnormal
Interpreter: Fix rounding edge case in frsp
| -rw-r--r-- | Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h | 19 |
1 files changed, 19 insertions, 0 deletions
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<u64>(value) & (Common::DOUBLE_EXP | Common::DOUBLE_FRAC); + + if (value_without_sign < smallest_normal_single) + { + const u64 flushed_double = Common::BitCast<u64>(value) & Common::DOUBLE_SIGN; + const u32 flushed_single = static_cast<u32>(flushed_double >> 32); + return Common::BitCast<float>(flushed_single); + } + } + + // Emulate standard conversion to single precision. + float x = static_cast<float>(value); if (!cpu_info.bFlushToZero && fpscr.NI) { |
