summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMai <mathew1800@gmail.com>2022-08-09 18:17:21 -0400
committerGitHub <noreply@github.com>2022-08-09 18:17:21 -0400
commitc2dd58c3615dbf89552e5f2fa5b283fa6fc56fff (patch)
tree6468a3df111f1be3697706b5c392a4c15d16f12c
parente9e2c741a20da47191ba4585fdd5c9a1d5e32f5a (diff)
parentc5d9514cd9d92ce1845de254e4ef92e740f8c645 (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.h19
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)
{