From f17cd6f2c535ab7835b6ed8fd2211284cc92a1a7 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Mon, 23 Jul 2018 17:41:13 +0100 Subject: [PATCH] unpacked: Use ResidualErrorOnRightShift in FPRoundBase Fixes a bug relating to exponents that are severely out of range. --- src/common/fp/unpacked.cpp | 25 ++++++++++++------------- tests/fp/unpacked_tests.cpp | 12 ++++++++++++ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/common/fp/unpacked.cpp b/src/common/fp/unpacked.cpp index 0cd478b4..f9efe994 100644 --- a/src/common/fp/unpacked.cpp +++ b/src/common/fp/unpacked.cpp @@ -6,6 +6,7 @@ #include "common/fp/fpsr.h" #include "common/fp/info.h" +#include "common/fp/mantissa_util.h" #include "common/fp/process_exception.h" #include "common/fp/rounding_mode.h" #include "common/fp/unpacked.h" @@ -55,11 +56,11 @@ template std::tuple FPUnpack(u32 op, FPCR fpcr, F template std::tuple FPUnpack(u64 op, FPCR fpcr, FPSR& fpsr); template -std::tuple Normalize(FPUnpacked op) { +std::tuple Normalize(FPUnpacked op, int extra_right_shift = 0) { const int highest_set_bit = Common::HighestSetBit(op.mantissa); - const int shift_amount = highest_set_bit - static_cast(F); + const int shift_amount = highest_set_bit - static_cast(F) + extra_right_shift; const u64 mantissa = Safe::LogicalShiftRight(op.mantissa, shift_amount); - const u64 error = Safe::LogicalShiftRightDouble(op.mantissa, static_cast(0), shift_amount); + const ResidualError error = ResidualErrorOnRightShift(op.mantissa, shift_amount); const int exponent = op.exponent + highest_set_bit; return std::make_tuple(op.sign, exponent, mantissa, error); } @@ -83,28 +84,26 @@ FPT FPRoundBase(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr) { int biased_exp = std::max(exponent - minimum_exp + 1, 0); if (biased_exp == 0) { - error = Safe::LogicalShiftRightDouble(mantissa, error, minimum_exp - exponent); - mantissa = Safe::LogicalShiftRight(mantissa, minimum_exp - exponent); + std::tie(sign, exponent, mantissa, error) = Normalize(op, minimum_exp - exponent); } - if (biased_exp == 0 && (error != 0 || fpcr.UFE())) { + if (biased_exp == 0 && (error != ResidualError::Zero || fpcr.UFE())) { FPProcessException(FPExc::Underflow, fpcr, fpsr); } bool round_up = false, overflow_to_inf = false; switch (rounding) { case RoundingMode::ToNearest_TieEven: { - constexpr u64 half = static_cast(1) << (Common::BitSize() - 1); - round_up = (error > half) || (error == half && Common::Bit<0>(mantissa)); + round_up = (error > ResidualError::Half) || (error == ResidualError::Half && Common::Bit<0>(mantissa)); overflow_to_inf = true; break; } case RoundingMode::TowardsPlusInfinity: - round_up = error != 0 && !sign; + round_up = error != ResidualError::Zero && !sign; overflow_to_inf = !sign; break; case RoundingMode::TowardsMinusInfinity: - round_up = error != 0 && sign; + round_up = error != ResidualError::Zero && sign; overflow_to_inf = sign; break; default: @@ -128,7 +127,7 @@ FPT FPRoundBase(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr) { } } - if (error != 0 && rounding == RoundingMode::ToOdd) { + if (error != ResidualError::Zero && rounding == RoundingMode::ToOdd) { mantissa = Common::ModifyBit<0>(mantissa, true); } @@ -152,7 +151,7 @@ FPT FPRoundBase(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr) { result += biased_exp; result <<= F; result |= static_cast(mantissa) & FPInfo::mantissa_mask; - if (error != 0) { + if (error != ResidualError::Zero) { FPProcessException(FPExc::Inexact, fpcr, fpsr); } } @@ -167,7 +166,7 @@ FPT FPRoundBase(FPUnpacked op, FPCR fpcr, RoundingMode rounding, FPSR& fpsr) { result += biased_exp; result <<= F; result |= static_cast(mantissa) & FPInfo::mantissa_mask; - if (error != 0) { + if (error != ResidualError::Zero) { FPProcessException(FPExc::Inexact, fpcr, fpsr); } } diff --git a/tests/fp/unpacked_tests.cpp b/tests/fp/unpacked_tests.cpp index 2b50b396..1f7cd12c 100644 --- a/tests/fp/unpacked_tests.cpp +++ b/tests/fp/unpacked_tests.cpp @@ -75,3 +75,15 @@ TEST_CASE("FPUnpack<->FPRound Round-trip Tests", "[fp]") { REQUIRE(input == output); } } + +TEST_CASE("FPRound (near zero, round to posinf)", "[fp]") { + const FPUnpacked input = {false, -353, 0x0a98d25ace5b2000}; + + FPSR fpsr; + FPCR fpcr; + fpcr.RMode(RoundingMode::TowardsPlusInfinity); + + const u32 output = FPRound(input, fpcr, fpsr); + + REQUIRE(output == 0x00000001); +}