diff --git a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp index 8036a079f6..b1dbbf0cc1 100644 --- a/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp +++ b/Source/Core/Core/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp @@ -8,6 +8,7 @@ #include "Common/CommonTypes.h" #include "Common/FloatUtils.h" +#include "Common/Unreachable.h" #include "Core/PowerPC/Gekko.h" #include "Core/PowerPC/Interpreter/Interpreter_FPUtils.h" #include "Core/PowerPC/PowerPC.h" @@ -32,6 +33,22 @@ void SetFI(PowerPC::PowerPCState& ppc_state, u32 FI) ppc_state.fpscr.FI = FI; } +// Round a number to an integer in the same direction as the CPU rounding mode, +// without setting any CPU flags or being careful about NaNs +double RoundToIntegerMode(double number) +{ + // This value is 2^52 -- The first number in which double precision floating point + // numbers can only store subsequent integers, and no longer any decimals + // This keeps the sign of the unrounded value because it needs to scale it + // upwards when added + const double int_precision = std::copysign(4503599627370496.0, number); + + // By adding this value to the original number, + // it will be forced to decide a integer to round to + // This rounding will be the same as the CPU rounding mode + return (number + int_precision) - int_precision; +} + // Note that the convert to integer operation is defined // in Appendix C.4.2 in PowerPC Microprocessor Family: // The Programming Environments Manual for 32 and 64-bit Microprocessors @@ -39,9 +56,34 @@ void ConvertToInteger(PowerPC::PowerPCState& ppc_state, UGeckoInstruction inst, RoundingMode rounding_mode) { const double b = ppc_state.ps[inst.FB].PS0AsDouble(); + double rounded; u32 value; bool exception_occurred = false; + // To reduce complexity, this takes in a rounding mode in a switch case, + // rather than always judging based on the emulated CPU rounding mode + switch (rounding_mode) + { + case RoundingMode::Nearest: + // On generic platforms, the rounding should be assumed to be ties to even + // For targeted platforms this would work for any rounding mode, + // but it's mainly just kept in to replace roundeven, + // due to its lack in the C++17 (and possible lack for future versions) + rounded = RoundToIntegerMode(b); + break; + case RoundingMode::TowardsZero: + rounded = std::trunc(b); + break; + case RoundingMode::TowardsPositiveInfinity: + rounded = std::ceil(b); + break; + case RoundingMode::TowardsNegativeInfinity: + rounded = std::floor(b); + break; + default: + Common::Unreachable(); + } + if (std::isnan(b)) { if (Common::IsSNAN(b)) @@ -51,14 +93,14 @@ void ConvertToInteger(PowerPC::PowerPCState& ppc_state, UGeckoInstruction inst, SetFPException(ppc_state, FPSCR_VXCVI); exception_occurred = true; } - else if (b > static_cast(0x7fffffff)) + else if (rounded >= static_cast(0x80000000)) { // Positive large operand or +inf value = 0x7fffffff; SetFPException(ppc_state, FPSCR_VXCVI); exception_occurred = true; } - else if (b < -static_cast(0x80000000)) + else if (rounded < -static_cast(0x80000000)) { // Negative large operand or -inf value = 0x80000000; @@ -67,41 +109,9 @@ void ConvertToInteger(PowerPC::PowerPCState& ppc_state, UGeckoInstruction inst, } else { - s32 i = 0; - switch (rounding_mode) - { - case RoundingMode::Nearest: - { - const double t = b + 0.5; - i = static_cast(t); - - // Ties to even - if (t - i < 0 || (t - i == 0 && (i & 1))) - { - i--; - } - break; - } - case RoundingMode::TowardsZero: - i = static_cast(b); - break; - case RoundingMode::TowardsPositiveInfinity: - i = static_cast(b); - if (b - i > 0) - { - i++; - } - break; - case RoundingMode::TowardsNegativeInfinity: - i = static_cast(b); - if (b - i < 0) - { - i--; - } - break; - } - value = static_cast(i); - const double di = i; + s32 signed_value = static_cast(rounded); + value = static_cast(signed_value); + const double di = static_cast(signed_value); if (di == b) { ppc_state.fpscr.ClearFIFR();