From bc517d6058e4cbb6c319285f8b695fd6c462f1d0 Mon Sep 17 00:00:00 2001 From: AlxcBTW <187763517+AlxcBTW@users.noreply.github.com> Date: Sat, 10 Oct 2026 14:27:07 +0200 Subject: [PATCH] fix(shader): give an exact zero mix f16 sum -0 when rounding toward -infinity With f16 rounding toward -infinity, v_fma_mixlo_f16 and v_fma_mixhi_f16 gave +0 for an exact cancellation such as 1.0 * 1.0 + -1.0, because the f64 sum is computed to nearest. RDNA2 gives -0 there, as IEEE 754 does for an exact zero sum rounded toward -infinity, measured on gfx1036 in #2631. An exact zero sum now gives -0 in that mode unless both the product and the addend are +0. A nonzero result that rounds to zero keeps its sign, so a tiny positive sum still gives +0. Co-Authored-By: Claude Opus 5.5 --- .../prx/libSceAgcDriver/tests/execution/MixPrecision.cpp | 8 +++++++- .../SpirvBackend/src/SpirvAlu/SpirvAluEmitterFloat64.cpp | 9 +++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/MixPrecision.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/MixPrecision.cpp index 6c953b8da8..f7d68a606b 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/MixPrecision.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/MixPrecision.cpp @@ -121,7 +121,7 @@ struct DirectedRow { std::array expected; }; -constexpr std::array DirectedRows{{ +constexpr std::array DirectedRows{{ {0xbfc00000u, 0x33800000u, 0x00800000u, {0x8001u, 0x8001u, 0x8002u, 0x8001u}}, {0x3fc00000u, 0x33800000u, 0x00800000u, {0x0002u, 0x0002u, 0x0001u, 0x0001u}}, {0x3f800000u, 0x3f801400u, 0x00000000u, {0x3c01u, 0x3c01u, 0x3c00u, 0x3c00u}}, @@ -132,6 +132,12 @@ constexpr std::array DirectedRows{{ {0x3fc00000u, 0x40000000u, 0x3e800000u, {0x4280u, 0x4280u, 0x4280u, 0x4280u}}, {0x3f800000u, 0x3f800000u, 0xbf7fffffu, {0x0001u, 0x0001u, 0x0001u, 0x0001u}}, {0xbf800000u, 0x3f800000u, 0x3f7fffffu, {0x8001u, 0x8001u, 0x8001u, 0x8001u}}, + {0x3f800000u, 0x3f800000u, 0xbf800000u, {0x0000u, 0x0000u, 0x8000u, 0x0000u}}, + {0x40000000u, 0x3f800000u, 0xc0000000u, {0x0000u, 0x0000u, 0x8000u, 0x0000u}}, + {0x00000000u, 0x3f800000u, 0x80000000u, {0x0000u, 0x0000u, 0x8000u, 0x0000u}}, + {0x80000000u, 0x3f800000u, 0x80000000u, {0x8000u, 0x8000u, 0x8000u, 0x8000u}}, + {0x33000000u, 0x3f000000u, 0x00000000u, {0x0000u, 0x0001u, 0x0000u, 0x0000u}}, + {0xb3000000u, 0x3f000000u, 0x00000000u, {0x8000u, 0x8000u, 0x8001u, 0x8000u}}, }}; float Quarter(std::uint32_t index) { diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvAlu/SpirvAluEmitterFloat64.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvAlu/SpirvAluEmitterFloat64.cpp index 1ecf00b144..241522188c 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvAlu/SpirvAluEmitterFloat64.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvAlu/SpirvAluEmitterFloat64.cpp @@ -298,6 +298,15 @@ std::uint32_t Interpolate(SpirvEmitterState& state, std::uint32_t delta, std::ui if (rounding16 == 1u) limit = Select(state, u32, negative, ConstantU32(state, 0x40effc00u), ConstantU32(state, 0x40f00000u)); if (rounding16 == 2u) limit = Select(state, u32, negative, ConstantU32(state, 0x40f00000u), ConstantU32(state, 0x40effc00u)); rounded = ToF64(state, Join(state, SelectBits(state, overflow, {ConstantU32(state, 0u), Binary(state, spv::OpBitwiseOr, u32, sign, limit)}, bits))); + if (rounding16 == 2u) { + const auto boolean = TypeBool(state); + const auto zeroBits = [&](F64Bits value, std::uint32_t highMask) { + return Binary(state, spv::OpLogicalAnd, boolean, Binary(state, spv::OpIEqual, boolean, Binary(state, spv::OpBitwiseAnd, u32, value.high, ConstantU32(state, highMask)), ConstantU32(state, 0u)), Binary(state, spv::OpIEqual, boolean, value.low, ConstantU32(state, 0u))); + }; + const auto positiveZeros = Binary(state, spv::OpLogicalAnd, boolean, zeroBits(Split(state, FromF64(state, product)), 0xffffffffu), zeroBits(Split(state, FromF64(state, FromF32Exact(state, base))), 0xffffffffu)); + const auto negativeZero = Binary(state, spv::OpLogicalAnd, boolean, zeroBits(Split(state, FromF64(state, sum)), 0x7fffffffu), Unary(state, spv::OpLogicalNot, boolean, positiveZeros)); + rounded = ToF64(state, Join(state, SelectBits(state, negativeZero, {ConstantU32(state, 0u), ConstantU32(state, 0x80000000u)}, Split(state, FromF64(state, rounded))))); + } } } const auto finite = F32BitsOfRounded(state, rounded);