fp/op: Implement FPToFixed
This commit is contained in:
parent
8087e8df05
commit
9571269552
5 changed files with 163 additions and 0 deletions
|
@ -19,6 +19,8 @@ add_library(dynarmic
|
|||
common/fp/fpsr.h
|
||||
common/fp/info.h
|
||||
common/fp/mantissa_util.h
|
||||
common/fp/op.cpp
|
||||
common/fp/op.h
|
||||
common/fp/process_exception.cpp
|
||||
common/fp/process_exception.h
|
||||
common/fp/rounding_mode.h
|
||||
|
|
101
src/common/fp/op.cpp
Normal file
101
src/common/fp/op.cpp
Normal file
|
@ -0,0 +1,101 @@
|
|||
/* 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 "common/assert.h"
|
||||
#include "common/bit_util.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/safe_ops.h"
|
||||
#include "common/fp/fpsr.h"
|
||||
#include "common/fp/mantissa_util.h"
|
||||
#include "common/fp/op.h"
|
||||
#include "common/fp/process_exception.h"
|
||||
#include "common/fp/rounding_mode.h"
|
||||
#include "common/fp/unpacked.h"
|
||||
#include "frontend/A64/FPCR.h"
|
||||
|
||||
namespace Dynarmic::FP {
|
||||
|
||||
template<typename FPT>
|
||||
u64 FPToFixed(size_t ibits, FPT op, size_t fbits, bool unsigned_, FPCR fpcr, RoundingMode rounding, FPSR& fpsr) {
|
||||
ASSERT(rounding != RoundingMode::ToOdd);
|
||||
ASSERT(ibits <= 64);
|
||||
ASSERT(fbits <= ibits);
|
||||
|
||||
auto [type, sign, value] = FPUnpack<FPT>(op, fpcr, fpsr);
|
||||
|
||||
if (type == FPType::SNaN || type == FPType::QNaN) {
|
||||
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
|
||||
}
|
||||
|
||||
// Handle zero
|
||||
if (value.mantissa == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (sign && unsigned_) {
|
||||
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// value *= 2.0^fbits
|
||||
value.exponent += static_cast<int>(fbits);
|
||||
|
||||
u64 int_result = sign ? Safe::Negate<u64>(value.mantissa) : static_cast<u64>(value.mantissa);
|
||||
const ResidualError error = ResidualErrorOnRightShift(int_result, -value.exponent);
|
||||
int_result = Safe::ArithmeticShiftLeft(int_result, value.exponent);
|
||||
|
||||
bool round_up = false;
|
||||
switch (rounding) {
|
||||
case RoundingMode::ToNearest_TieEven:
|
||||
round_up = error > ResidualError::Half || (error == ResidualError::Half && Common::Bit<0>(int_result));
|
||||
break;
|
||||
case RoundingMode::TowardsPlusInfinity:
|
||||
round_up = error != ResidualError::Zero;
|
||||
break;
|
||||
case RoundingMode::TowardsMinusInfinity:
|
||||
round_up = false;
|
||||
break;
|
||||
case RoundingMode::TowardsZero:
|
||||
round_up = error != ResidualError::Zero && Common::MostSignificantBit(int_result);
|
||||
break;
|
||||
case RoundingMode::ToNearest_TieAwayFromZero:
|
||||
round_up = error > ResidualError::Half || (error == ResidualError::Half && !Common::MostSignificantBit(int_result));
|
||||
break;
|
||||
case RoundingMode::ToOdd:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
if (round_up) {
|
||||
int_result++;
|
||||
}
|
||||
|
||||
// Detect Overflow
|
||||
const int min_exponent_for_overflow = static_cast<int>(ibits) - static_cast<int>(Common::HighestSetBit(value.mantissa + (round_up ? 1 : 0))) - (unsigned_ ? 0 : 1);
|
||||
if (value.exponent >= min_exponent_for_overflow) {
|
||||
// Positive overflow
|
||||
if (unsigned_ || !sign) {
|
||||
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
|
||||
return Common::Ones<u64>(ibits - (unsigned_ ? 0 : 1));
|
||||
}
|
||||
|
||||
// Negative overflow
|
||||
const u64 min_value = Safe::Negate<u64>(static_cast<u64>(1) << (ibits - 1));
|
||||
if (!(value.exponent == min_exponent_for_overflow && int_result == min_value)) {
|
||||
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
|
||||
return static_cast<u64>(1) << (ibits - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (error != ResidualError::Zero) {
|
||||
FPProcessException(FPExc::Inexact, fpcr, fpsr);
|
||||
}
|
||||
return int_result & Common::Ones<u64>(ibits);
|
||||
}
|
||||
|
||||
template u64 FPToFixed<u32>(size_t ibits, u32 op, size_t fbits, bool unsigned_, FPCR fpcr, RoundingMode rounding, FPSR& fpsr);
|
||||
template u64 FPToFixed<u64>(size_t ibits, u64 op, size_t fbits, bool unsigned_, FPCR fpcr, RoundingMode rounding, FPSR& fpsr);
|
||||
|
||||
} // namespace Dynarmic::FP
|
21
src/common/fp/op.h
Normal file
21
src/common/fp/op.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/* 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/common_types.h"
|
||||
#include "common/fp/fpsr.h"
|
||||
#include "common/fp/rounding_mode.h"
|
||||
#include "frontend/A64/FPCR.h"
|
||||
|
||||
namespace Dynarmic::FP {
|
||||
|
||||
using FPCR = A64::FPCR;
|
||||
|
||||
template<typename FPT>
|
||||
u64 FPToFixed(size_t ibits, FPT op, size_t fbits, bool unsigned_, FPCR fpcr, RoundingMode rounding, FPSR& fpsr);
|
||||
|
||||
} // namespace Dynarmic::FP
|
|
@ -29,6 +29,7 @@ add_executable(dynarmic_tests
|
|||
A64/inst_gen.cpp
|
||||
A64/inst_gen.h
|
||||
A64/testenv.h
|
||||
fp/FPToFixed.cpp
|
||||
fp/mantissa_util_tests.cpp
|
||||
fp/unpacked_tests.cpp
|
||||
main.cpp
|
||||
|
|
38
tests/fp/FPToFixed.cpp
Normal file
38
tests/fp/FPToFixed.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* 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/fpsr.h"
|
||||
#include "common/fp/op.h"
|
||||
#include "rand_int.h"
|
||||
|
||||
using namespace Dynarmic;
|
||||
using namespace Dynarmic::FP;
|
||||
|
||||
TEST_CASE("FPToFixed", "[fp]") {
|
||||
const std::vector<std::tuple<u32, size_t, u64, u32>> test_cases {
|
||||
{0x447A0000, 64, 0x000003E8, 0x00},
|
||||
{0xC47A0000, 32, 0xFFFFFC18, 0x00},
|
||||
{0x4479E000, 64, 0x000003E8, 0x10},
|
||||
{0x50800000, 32, 0x7FFFFFFF, 0x01},
|
||||
{0xD0800000, 32, 0x80000000, 0x01},
|
||||
{0xCF000000, 32, 0x80000000, 0x00},
|
||||
{0x80002B94, 64, 0x00000000, 0x10},
|
||||
{0x80636D24, 64, 0x00000000, 0x10},
|
||||
};
|
||||
|
||||
const FPCR fpcr;
|
||||
for (auto [input, ibits, expected_output, expected_fpsr] : test_cases) {
|
||||
FPSR fpsr;
|
||||
const u64 output = FPToFixed<u32>(ibits, input, 0, false, fpcr, RoundingMode::ToNearest_TieEven, fpsr);
|
||||
REQUIRE(output == expected_output);
|
||||
REQUIRE(fpsr.Value() == expected_fpsr);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue