From 8b4ab86619eb88289b712d4c6bdfc690d7751daf Mon Sep 17 00:00:00 2001 From: lenamonj Date: Mon, 7 Sep 2026 21:06:30 -0400 Subject: [PATCH] Fix float16_to_float(vint4) decoding every negative half as NaN on F16C builds The F16C path narrowed the four lanes with _mm_packs_epi32, which saturates signed, so any half code with the sign bit set (0x8000 to 0xFFFF, a lane value above 0x7FFF) became 0x7FFF and decoded as NaN. Pack with _mm_packus_epi32 instead: the lanes hold zero-extended 16-bit codes, so the unsigned pack keeps every one of them. The unit test now covers negative halves, which the AVX2 unit binary fails without this change. --- Source/UnitTest/test_simd.cpp | 10 ++++++++++ Source/astcenc_vecmathlib_sse_4.h | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Source/UnitTest/test_simd.cpp b/Source/UnitTest/test_simd.cpp index b72563d1..913cc1f7 100644 --- a/Source/UnitTest/test_simd.cpp +++ b/Source/UnitTest/test_simd.cpp @@ -1154,6 +1154,16 @@ TEST(SuiteVfloat4, float16_to_float) // NaNs must be preserved EXPECT_NE(std::isnan(r.lane<3>()), 0); + + // Negative numbers must keep their sign + vint4 b(0xBE00, 0xDB54, 0xFC00, 0x8000); + vfloat4 s = float16_to_float(b); + + EXPECT_EQ(s.lane<0>(), -1.5); + EXPECT_EQ(s.lane<1>(), -234.5); + EXPECT_NE(std::isinf(s.lane<2>()), 0); + EXPECT_LT(s.lane<2>(), 0.0f); + EXPECT_NE(std::signbit(s.lane<3>()), 0); } /** @brief Test fp16 to float conversion. */ diff --git a/Source/astcenc_vecmathlib_sse_4.h b/Source/astcenc_vecmathlib_sse_4.h index c0eb6553..8aa9cc97 100644 --- a/Source/astcenc_vecmathlib_sse_4.h +++ b/Source/astcenc_vecmathlib_sse_4.h @@ -998,7 +998,7 @@ static inline uint16_t float_to_float16(float a) ASTCENC_SIMD_INLINE vfloat4 float16_to_float(vint4 a) { #if ASTCENC_F16C >= 1 - __m128i packed = _mm_packs_epi32(a.m, a.m); + __m128i packed = _mm_packus_epi32(a.m, a.m); __m128 f32 = _mm_cvtph_ps(packed); return vfloat4(f32); #else