[Testing] add test coverage for SET_ROUNDING_MODE
This commit is contained in:
99
src/xenia/cpu/testing/set_rounding_mode_test.cc
Normal file
99
src/xenia/cpu/testing/set_rounding_mode_test.cc
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2026 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/cpu/testing/util.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::hir;
|
||||
using namespace xe::cpu::testing;
|
||||
using xe::cpu::ppc::PPCContext;
|
||||
|
||||
// PPC rounding mode values (as passed to SET_ROUNDING_MODE, bits 0-1):
|
||||
// 0 = Round to nearest (ties to even)
|
||||
// 1 = Round toward zero (truncate)
|
||||
// 2 = Round toward +infinity (ceiling)
|
||||
// 3 = Round toward -infinity (floor)
|
||||
|
||||
// Test that SET_ROUNDING_MODE actually changes the FPU rounding behavior.
|
||||
// We add 1.0f + 2^-24 in f32. In round-to-nearest the result is 1.0f
|
||||
// (the half-ULP bit rounds to even). In round-toward-positive-infinity
|
||||
// the result is nextafterf(1.0f, 2.0f) = 1.0f + 2^-23.
|
||||
TEST_CASE("SET_ROUNDING_MODE_TOWARD_POS_INF", "[instr]") {
|
||||
// Round toward +infinity (mode 2), then add.
|
||||
TestFunction test([](HIRBuilder& b) {
|
||||
b.SetRoundingMode(b.LoadConstantInt32(2)); // toward +inf
|
||||
auto a = b.Convert(LoadFPR(b, 4), FLOAT32_TYPE);
|
||||
auto c = b.Convert(LoadFPR(b, 5), FLOAT32_TYPE);
|
||||
auto sum = b.Add(a, c);
|
||||
StoreFPR(b, 3, b.Convert(sum, FLOAT64_TYPE));
|
||||
b.Return();
|
||||
});
|
||||
test.Run(
|
||||
[](PPCContext* ctx) {
|
||||
ctx->f[4] = 1.0;
|
||||
// 2^-24 = 5.960464477539063e-08
|
||||
ctx->f[5] = std::ldexp(1.0, -24);
|
||||
},
|
||||
[](PPCContext* ctx) {
|
||||
auto result = static_cast<float>(ctx->f[3]);
|
||||
// With round-toward-positive-infinity, 1.0f + 2^-24 should round
|
||||
// up to the next representable float above 1.0f.
|
||||
float expected = std::nextafterf(1.0f, 2.0f);
|
||||
REQUIRE(result == expected);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("SET_ROUNDING_MODE_TOWARD_ZERO", "[instr]") {
|
||||
// Round toward zero (mode 1), then add a positive value.
|
||||
// For positive results, toward-zero == toward-negative-infinity (truncate).
|
||||
TestFunction test([](HIRBuilder& b) {
|
||||
b.SetRoundingMode(b.LoadConstantInt32(1)); // toward zero
|
||||
auto a = b.Convert(LoadFPR(b, 4), FLOAT32_TYPE);
|
||||
auto c = b.Convert(LoadFPR(b, 5), FLOAT32_TYPE);
|
||||
auto sum = b.Add(a, c);
|
||||
StoreFPR(b, 3, b.Convert(sum, FLOAT64_TYPE));
|
||||
b.Return();
|
||||
});
|
||||
test.Run(
|
||||
[](PPCContext* ctx) {
|
||||
ctx->f[4] = 1.0;
|
||||
ctx->f[5] = std::ldexp(1.0, -24);
|
||||
},
|
||||
[](PPCContext* ctx) {
|
||||
auto result = static_cast<float>(ctx->f[3]);
|
||||
// With round-toward-zero, 1.0f + 2^-24 should truncate to 1.0f.
|
||||
REQUIRE(result == 1.0f);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("SET_ROUNDING_MODE_NEAREST", "[instr]") {
|
||||
// Round to nearest (mode 0) — default.
|
||||
TestFunction test([](HIRBuilder& b) {
|
||||
b.SetRoundingMode(b.LoadConstantInt32(0)); // nearest
|
||||
auto a = b.Convert(LoadFPR(b, 4), FLOAT32_TYPE);
|
||||
auto c = b.Convert(LoadFPR(b, 5), FLOAT32_TYPE);
|
||||
auto sum = b.Add(a, c);
|
||||
StoreFPR(b, 3, b.Convert(sum, FLOAT64_TYPE));
|
||||
b.Return();
|
||||
});
|
||||
test.Run(
|
||||
[](PPCContext* ctx) {
|
||||
ctx->f[4] = 1.0;
|
||||
ctx->f[5] = std::ldexp(1.0, -24);
|
||||
},
|
||||
[](PPCContext* ctx) {
|
||||
auto result = static_cast<float>(ctx->f[3]);
|
||||
// With round-to-nearest, 1.0f + 2^-24 rounds to 1.0f (ties to even).
|
||||
REQUIRE(result == 1.0f);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user