summaryrefslogtreecommitdiff
path: root/Source/Core
diff options
context:
space:
mode:
authorGeotale <Minemaker2006@gmail.com>2024-05-27 17:38:35 -0500
committerGeotale <Minemaker2006@gmail.com>2024-09-03 11:33:32 -0500
commitffa680b15c839a6816361dab133a390e581ca679 (patch)
treee00231d4c1c39b0be2af3f351d3c2120a149a54c /Source/Core
parent85826440580a4e90e3fda9d9f08116b4e5b1981f (diff)
Proper Subnormal Rounding When Interpreting
During 25-bit rounding, subnormals are "normalized" This would normally mean that the exponent needs to be able to be <-1023 Instead, you can modify at what bit you round and get the same results! This is done by finding the highest bit and shifting right the round bit Co-Authored-By: JosJuice <josjuice@gmail.com>
Diffstat (limited to 'Source/Core')
-rw-r--r--Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h28
1 files changed, 27 insertions, 1 deletions
diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h
index 2f9406ff05..67b6af7560 100644
--- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h
+++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FPUtils.h
@@ -92,7 +92,33 @@ inline double Force25Bit(double d)
{
u64 integral = std::bit_cast<u64>(d);
- integral = (integral & 0xFFFFFFFFF8000000ULL) + (integral & 0x8000000);
+ u64 exponent = integral & Common::DOUBLE_EXP;
+ u64 fraction = integral & Common::DOUBLE_FRAC;
+
+ if (exponent == 0 && fraction != 0)
+ {
+ // Subnormals get "normalized" before they're rounded
+ // In the end, this practically just means that the rounding is
+ // at a different bit
+
+ s64 keep_mask = 0xFFFFFFFFF8000000LL;
+ u64 round = 0x8000000;
+
+ // Shift the mask and rounding bit to the right until
+ // the fraction is "normal"
+ // That is to say shifting it until the MSB of the fraction
+ // would escape into the exponent
+ u32 shift = std::countl_zero(fraction) - (63 - Common::DOUBLE_FRAC_WIDTH);
+ keep_mask >>= shift;
+ round >>= shift;
+
+ // Round using these shifted values
+ integral = (integral & keep_mask) + (integral & round);
+ }
+ else
+ {
+ integral = (integral & 0xFFFFFFFFF8000000ULL) + (integral & 0x8000000);
+ }
return std::bit_cast<double>(integral);
}