mantissa_util: Implement ResidualErrorOnRightShift

Accurately calculate residual error that is shifted out
This commit is contained in:
MerryMage 2018-06-29 18:52:48 +01:00
parent 52ed365158
commit 8087e8df05
4 changed files with 113 additions and 0 deletions

View file

@ -18,6 +18,7 @@ add_library(dynarmic
common/crc32.h
common/fp/fpsr.h
common/fp/info.h
common/fp/mantissa_util.h
common/fp/process_exception.cpp
common/fp/process_exception.h
common/fp/rounding_mode.h

View file

@ -0,0 +1,48 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#pragma once
#include "common/bit_util.h"
#include "common/common_types.h"
namespace Dynarmic::FP {
enum class ResidualError {
Zero,
LessThanHalf,
Half,
GreaterThanHalf,
};
template<typename MantissaT>
ResidualError ResidualErrorOnRightShift(MantissaT mantissa, int shift_amount) {
if (shift_amount <= 0 || mantissa == 0) {
return ResidualError::Zero;
}
if (shift_amount > static_cast<int>(Common::BitSize<MantissaT>())) {
return Common::MostSignificantBit(mantissa) ? ResidualError::GreaterThanHalf : ResidualError::LessThanHalf;
}
const size_t half_bit_position = static_cast<size_t>(shift_amount - 1);
const MantissaT half = static_cast<MantissaT>(1) << half_bit_position;
const MantissaT error_mask = Common::Ones<MantissaT>(static_cast<size_t>(shift_amount));
const MantissaT error = mantissa & error_mask;
if (error == 0) {
return ResidualError::Zero;
}
if (error < half) {
return ResidualError::LessThanHalf;
}
if (error == half) {
return ResidualError::Half;
}
return ResidualError::GreaterThanHalf;
}
} // namespace Dynarmic::FP

View file

@ -29,6 +29,7 @@ add_executable(dynarmic_tests
A64/inst_gen.cpp
A64/inst_gen.h
A64/testenv.h
fp/mantissa_util_tests.cpp
fp/unpacked_tests.cpp
main.cpp
mp.cpp

View file

@ -0,0 +1,63 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* This software may be used and distributed according to the terms of the GNU
* General Public License version 2 or any later version.
*/
#include <tuple>
#include <vector>
#include <catch.hpp>
#include "common/fp/mantissa_util.h"
#include "common/safe_ops.h"
#include "rand_int.h"
using namespace Dynarmic;
using namespace Dynarmic::FP;
TEST_CASE("ResidualErrorOnRightShift", "[fp]") {
const std::vector<std::tuple<u32, int, ResidualError>> test_cases {
{0x00000001, 1, ResidualError::Half},
{0x00000002, 1, ResidualError::Zero},
{0x00000001, 2, ResidualError::LessThanHalf},
{0x00000002, 2, ResidualError::Half},
{0x00000003, 2, ResidualError::GreaterThanHalf},
{0x00000004, 2, ResidualError::Zero},
{0x00000005, 2, ResidualError::LessThanHalf},
{0x00000006, 2, ResidualError::Half},
{0x00000007, 2, ResidualError::GreaterThanHalf},
};
for (auto [mantissa, shift, expected_result] : test_cases) {
const ResidualError result = ResidualErrorOnRightShift(mantissa, shift);
REQUIRE(result == expected_result);
}
}
TEST_CASE("ResidualErrorOnRightShift Randomized", "[fp]") {
for (size_t test = 0; test < 100000; test++) {
const u32 mantissa = RandInt<u32>(0, 0xFFFFFFFF);
const int shift = RandInt<int>(-60, 60);
const ResidualError result = ResidualErrorOnRightShift(mantissa, shift);
const u64 calculated_error = Safe::ArithmeticShiftRightDouble(Common::SignExtend<32, u64>(mantissa), u64(0), shift);
const ResidualError expected_result = [&]{
constexpr u64 half_error = 0x8000'0000'0000'0000ull;
if (calculated_error == 0) {
return ResidualError::Zero;
}
if (calculated_error < half_error) {
return ResidualError::LessThanHalf;
}
if (calculated_error == half_error) {
return ResidualError::Half;
}
return ResidualError::GreaterThanHalf;
}();
INFO(std::hex << "mantissa " << mantissa << " shift " << shift << " calculated_error " << calculated_error);
REQUIRE(result == expected_result);
}
}