[libc-commits] [libc] [libc][stdfix] Implement mulifx functions (PR #235602)
Jaeyong Lee via libc-commits
libc-commits at lists.llvm.org
Mon Oct 26 06:47:05 PDT 2026
https://github.com/npmania updated https://github.com/llvm/llvm-project/pull/235602
>From d615b2cf33371d959bcae252796dcf05739994c3 Mon Sep 17 00:00:00 2001
From: Jaeyong Lee <jay at glendalab.com>
Date: Mon, 26 Oct 2026 00:30:22 +0900
Subject: [PATCH 1/6] [libc][stdfix] Implement mulik function
---
libc/config/baremetal/arm/entrypoints.txt | 1 +
libc/config/baremetal/hexagon/entrypoints.txt | 1 +
libc/config/baremetal/riscv/entrypoints.txt | 1 +
libc/config/linux/riscv/entrypoints.txt | 1 +
libc/config/linux/x86_64/entrypoints.txt | 1 +
libc/docs/headers/stdfix.md | 2 +-
libc/include/stdfix.yaml | 8 +
libc/src/__support/fixed_point/fx_bits.h | 42 +++++
libc/src/stdfix/CMakeLists.txt | 12 ++
libc/src/stdfix/mulik.cpp | 21 +++
libc/src/stdfix/mulik.h | 21 +++
libc/test/src/stdfix/CMakeLists.txt | 15 ++
libc/test/src/stdfix/MuliFxTest.h | 154 ++++++++++++++++++
libc/test/src/stdfix/mulik_test.cpp | 14 ++
14 files changed, 293 insertions(+), 1 deletion(-)
create mode 100644 libc/src/stdfix/mulik.cpp
create mode 100644 libc/src/stdfix/mulik.h
create mode 100644 libc/test/src/stdfix/MuliFxTest.h
create mode 100644 libc/test/src/stdfix/mulik_test.cpp
diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt
index b7322502d570d4..04bbd681e73b4f 100644
--- a/libc/config/baremetal/arm/entrypoints.txt
+++ b/libc/config/baremetal/arm/entrypoints.txt
@@ -998,6 +998,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.ulrdivi
libc.src.stdfix.ukdivi
libc.src.stdfix.ulkdivi
+ libc.src.stdfix.mulik
)
endif()
diff --git a/libc/config/baremetal/hexagon/entrypoints.txt b/libc/config/baremetal/hexagon/entrypoints.txt
index 61f869457b3282..164c806dfe4a5d 100644
--- a/libc/config/baremetal/hexagon/entrypoints.txt
+++ b/libc/config/baremetal/hexagon/entrypoints.txt
@@ -979,6 +979,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.diviulr
libc.src.stdfix.diviuk
libc.src.stdfix.diviulk
+ libc.src.stdfix.mulik
)
endif()
diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt
index 7e68a3c374249d..880a1e82aad85f 100644
--- a/libc/config/baremetal/riscv/entrypoints.txt
+++ b/libc/config/baremetal/riscv/entrypoints.txt
@@ -990,6 +990,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.ulrdivi
libc.src.stdfix.ukdivi
libc.src.stdfix.ulkdivi
+ libc.src.stdfix.mulik
)
endif()
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 273b865173bcec..f30388590af423 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1297,6 +1297,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.diviulr
libc.src.stdfix.diviuk
libc.src.stdfix.diviulk
+ libc.src.stdfix.mulik
)
endif()
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 1769cfbfaa9b43..f8deb2188b5c6a 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1319,6 +1319,7 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
libc.src.stdfix.diviulr
libc.src.stdfix.diviuk
libc.src.stdfix.diviulk
+ libc.src.stdfix.mulik
)
endif()
diff --git a/libc/docs/headers/stdfix.md b/libc/docs/headers/stdfix.md
index b689312642cdb1..3307bc05033014 100644
--- a/libc/docs/headers/stdfix.md
+++ b/libc/docs/headers/stdfix.md
@@ -60,7 +60,7 @@ The following functions are included in the ISO/IEC TR 18037:2008 standard.
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
| idiv\* | | | ✅ | ✅ | ✅ | ✅ | | | ✅ | ✅ | ✅ | ✅ |
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
-| muli | | | | | | | | | | | | |
+| muli\* | | | | | | | | | | ✅ | | |
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
| \*divi | | | ✅ | ✅ | ✅ | ✅ | | | ✅ | ✅ | ✅ | ✅ |
+---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
diff --git a/libc/include/stdfix.yaml b/libc/include/stdfix.yaml
index 739805ea2a39a7..2af1dac6468bb2 100644
--- a/libc/include/stdfix.yaml
+++ b/libc/include/stdfix.yaml
@@ -685,3 +685,11 @@ functions:
- type: unsigned long int
- type: unsigned long accum
guard: LIBC_COMPILER_HAS_FIXED_POINT
+ - name: mulik
+ standards:
+ - stdc_ext
+ return_type: int
+ arguments:
+ - type: int
+ - type: accum
+ guard: LIBC_COMPILER_HAS_FIXED_POINT
diff --git a/libc/src/__support/fixed_point/fx_bits.h b/libc/src/__support/fixed_point/fx_bits.h
index 5c3b39d5a64aad..6e91092adfed3e 100644
--- a/libc/src/__support/fixed_point/fx_bits.h
+++ b/libc/src/__support/fixed_point/fx_bits.h
@@ -404,6 +404,48 @@ divifx(IntType n, FXType d) {
return static_cast<IntType>(result);
}
+// Multiply an integer operand by a fixed-point operand and return the
+// mathematically exact result as an IntType rounded towards 0. Assumes
+// signedness of IntType matches the signedness of FXType.
+template <typename IntType, typename FXType>
+LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<FXType>, IntType>
+mulifx(IntType n, FXType m) {
+ using FXBits = FXBits<FXType>;
+ using FXRep = FXRep<FXType>;
+ using CompType = typename FXRep::CompType;
+
+ static_assert(cpp::is_signed_v<IntType> == (FXRep::SIGN_LEN > 0),
+ "IntType and FXType must have matching signedness");
+
+ if (LIBC_UNLIKELY(n == 0)) {
+ return static_cast<IntType>(0);
+ }
+
+ CompType m_comp = static_cast<CompType>(FXBits(m).get_bits());
+
+ constexpr int F = FXRep::FRACTION_LEN;
+
+ constexpr int INTERMEDIATE_BITS =
+ sizeof(IntType) * 8 - cpp::is_signed_v<IntType> + FXRep::VALUE_LEN;
+
+ using WideType = cpp::conditional_t<
+ cpp::is_signed_v<IntType>,
+ cpp::conditional_t<INTERMEDIATE_BITS <= 64, int64_t, Int128>,
+ cpp::conditional_t<INTERMEDIATE_BITS <= 64, uint64_t, UInt128>>;
+
+ WideType unscaled_result = n * static_cast<WideType>(m_comp);
+
+ if constexpr (FXRep::SIGN_LEN > 0) {
+ constexpr WideType MASK = (WideType(1) << F) - 1;
+ constexpr int SIGN_OFFSET = cpp::numeric_limits<WideType>::digits;
+ WideType result =
+ (unscaled_result + ((unscaled_result >> SIGN_OFFSET) & MASK)) >> F;
+ return static_cast<IntType>(result);
+ }
+ WideType result = unscaled_result >> F;
+ return static_cast<IntType>(result);
+}
+
} // namespace fixed_point
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/CMakeLists.txt b/libc/src/stdfix/CMakeLists.txt
index de171113993057..660f908aed458f 100644
--- a/libc/src/stdfix/CMakeLists.txt
+++ b/libc/src/stdfix/CMakeLists.txt
@@ -106,6 +106,18 @@ foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk)
)
endforeach()
+foreach(suffix IN ITEMS k)
+ add_entrypoint_object(
+ muli${suffix}
+ HDRS
+ muli${suffix}.h
+ SRCS
+ muli${suffix}.cpp
+ DEPENDS
+ libc.src.__support.fixed_point.fx_bits
+ )
+endforeach()
+
add_entrypoint_object(
uhksqrtus
HDRS
diff --git a/libc/src/stdfix/mulik.cpp b/libc/src/stdfix/mulik.cpp
new file mode 100644
index 00000000000000..4673d9fca76b2c
--- /dev/null
+++ b/libc/src/stdfix/mulik.cpp
@@ -0,0 +1,21 @@
+//===-- Implementation of mulik function ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "mulik.h"
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/common.h"
+#include "src/__support/fixed_point/fx_bits.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, mulik, (int n, accum m)) {
+ return fixed_point::mulifx<int, accum>(n, m);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/mulik.h b/libc/src/stdfix/mulik.h
new file mode 100644
index 00000000000000..001a2ebed1cdc2
--- /dev/null
+++ b/libc/src/stdfix/mulik.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for mulik -------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDFIX_MULIK_H
+#define LLVM_LIBC_SRC_STDFIX_MULIK_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int mulik(int n, accum m);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_MULIK_H
diff --git a/libc/test/src/stdfix/CMakeLists.txt b/libc/test/src/stdfix/CMakeLists.txt
index 391339cf3fbf5c..e1b5e8db4f5d28 100644
--- a/libc/test/src/stdfix/CMakeLists.txt
+++ b/libc/test/src/stdfix/CMakeLists.txt
@@ -156,6 +156,21 @@ foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk)
)
endforeach()
+foreach(suffix IN ITEMS k)
+ add_libc_test(
+ muli${suffix}_test
+ SUITE
+ libc-stdfix-tests
+ HDRS
+ MuliFxTest.h
+ SRCS
+ muli${suffix}_test.cpp
+ DEPENDS
+ libc.src.stdfix.muli${suffix}
+ libc.src.__support.fixed_point.fx_rep
+ )
+endforeach()
+
add_libc_test(
uhksqrtus_test
SUITE
diff --git a/libc/test/src/stdfix/MuliFxTest.h b/libc/test/src/stdfix/MuliFxTest.h
new file mode 100644
index 00000000000000..7404e48575bbfa
--- /dev/null
+++ b/libc/test/src/stdfix/MuliFxTest.h
@@ -0,0 +1,154 @@
+//===-- Utility class to test mulifx functions ------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "test/UnitTest/Test.h"
+
+#include "llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/CPP/limits.h"
+#include "src/__support/fixed_point/fx_rep.h"
+
+namespace cpp = LIBC_NAMESPACE::cpp;
+
+template <typename IntType, typename FXType>
+class MuliFxTest : public LIBC_NAMESPACE::testing::Test {
+
+ using FXRep = LIBC_NAMESPACE::fixed_point::FXRep<FXType>;
+
+ static constexpr FXType max = FXRep::MAX();
+ static constexpr FXType min = FXRep::MIN();
+ static constexpr FXType epsilon = FXRep::EPS();
+ static constexpr FXType one_half = FXRep::ONE_HALF();
+ static constexpr FXType one_fourth = FXRep::ONE_FOURTH();
+ static constexpr FXType one_eighth = FXRep::ONE_EIGHTH();
+
+ static constexpr bool is_signed = (FXRep::SIGN_LEN > 0);
+ static constexpr bool has_integral = (FXRep::INTEGRAL_LEN > 0);
+
+public:
+ typedef IntType (*MuliFxFunc)(IntType, FXType);
+
+ void testBasicNumbers(MuliFxFunc func) {
+
+ EXPECT_EQ(func(0, epsilon), static_cast<IntType>(0));
+ EXPECT_EQ(func(0, max), static_cast<IntType>(0));
+
+ EXPECT_EQ(func(1, one_eighth), static_cast<IntType>(0));
+ EXPECT_EQ(func(7, one_eighth), static_cast<IntType>(0));
+ EXPECT_EQ(func(8, one_eighth), static_cast<IntType>(1));
+ EXPECT_EQ(func(9, one_eighth), static_cast<IntType>(1));
+ EXPECT_EQ(func(16, one_eighth), static_cast<IntType>(2));
+ EXPECT_EQ(func(4, one_fourth), static_cast<IntType>(1));
+ EXPECT_EQ(func(8, one_fourth), static_cast<IntType>(2));
+ EXPECT_EQ(func(1, one_half), static_cast<IntType>(0));
+ EXPECT_EQ(func(3, one_half), static_cast<IntType>(1));
+ EXPECT_EQ(func(4, one_half), static_cast<IntType>(2));
+
+ // Verify rounding towards 0.
+ EXPECT_EQ(func(1, 3 * one_fourth), static_cast<IntType>(0));
+ EXPECT_EQ(func(2, 3 * one_fourth), static_cast<IntType>(1));
+ EXPECT_EQ(func(3, 3 * one_fourth), static_cast<IntType>(2));
+
+ if constexpr (is_signed) {
+ EXPECT_EQ(func(-1, one_half), static_cast<IntType>(0));
+ EXPECT_EQ(func(-3, one_half), static_cast<IntType>(-1));
+ EXPECT_EQ(func(3, -one_half), static_cast<IntType>(-1));
+ EXPECT_EQ(func(-3, -one_half), static_cast<IntType>(1));
+ EXPECT_EQ(func(-8, one_eighth), static_cast<IntType>(-1));
+ EXPECT_EQ(func(-9, one_eighth), static_cast<IntType>(-1));
+ EXPECT_EQ(func(-1, 3 * one_fourth), static_cast<IntType>(0));
+ EXPECT_EQ(func(-2, 3 * one_fourth), static_cast<IntType>(-1));
+ EXPECT_EQ(func(-3, 3 * one_fourth), static_cast<IntType>(-2));
+ }
+
+ if constexpr (has_integral) {
+ EXPECT_EQ(func(1, static_cast<FXType>(1)), static_cast<IntType>(1));
+ EXPECT_EQ(func(2, static_cast<FXType>(2)), static_cast<IntType>(4));
+
+ if constexpr (is_signed) {
+ EXPECT_EQ(func(1, static_cast<FXType>(-1)), static_cast<IntType>(-1));
+ EXPECT_EQ(func(-1, static_cast<FXType>(1)), static_cast<IntType>(-1));
+ EXPECT_EQ(func(-1, static_cast<FXType>(-1)), static_cast<IntType>(1));
+ }
+
+ // Only run these tests for accum types that can represent these operands.
+ constexpr FXType max_test_operand = static_cast<FXType>(4);
+
+ if constexpr (max >= max_test_operand) {
+ EXPECT_EQ(func(3, 2.5), static_cast<IntType>(7));
+ EXPECT_EQ(func(4, 1.5), static_cast<IntType>(6));
+
+ if constexpr (is_signed) {
+ EXPECT_EQ(func(-3, 2.5), static_cast<IntType>(-7));
+ EXPECT_EQ(func(-3, -2.5), static_cast<IntType>(7));
+ }
+ }
+ }
+ }
+
+ void testEdgeCases(MuliFxFunc func) {
+
+ constexpr IntType int_max = cpp::numeric_limits<IntType>::max();
+ constexpr IntType int_min = cpp::numeric_limits<IntType>::min();
+
+ if constexpr (has_integral) {
+ EXPECT_EQ(func(int_max, static_cast<FXType>(1)), int_max);
+
+ if constexpr (is_signed) {
+ EXPECT_EQ(func(int_max, static_cast<FXType>(-1)), -int_max);
+ }
+ }
+
+ EXPECT_EQ(func(int_max, one_half), int_max / 2);
+
+ if constexpr (is_signed) {
+ EXPECT_EQ(func(int_min, one_half), int_min / 2);
+ }
+
+ if constexpr (FXRep::FRACTION_LEN < cpp::numeric_limits<IntType>::digits) {
+ constexpr IntType epsilon_reciprocal = static_cast<IntType>(1)
+ << FXRep::FRACTION_LEN;
+ EXPECT_EQ(func(epsilon_reciprocal, epsilon), static_cast<IntType>(1));
+
+ if constexpr (is_signed) {
+ EXPECT_EQ(func(-epsilon_reciprocal, epsilon), static_cast<IntType>(-1));
+ EXPECT_EQ(func(epsilon_reciprocal, -epsilon), static_cast<IntType>(-1));
+ EXPECT_EQ(func(-epsilon_reciprocal, -epsilon), static_cast<IntType>(1));
+ }
+ }
+
+ if constexpr (has_integral &&
+ FXRep::INTEGRAL_LEN < cpp::numeric_limits<IntType>::digits) {
+ constexpr IntType integral_max =
+ (static_cast<IntType>(1) << FXRep::INTEGRAL_LEN) - 1;
+
+ EXPECT_EQ(func(1, max), integral_max);
+
+ if constexpr (is_signed) {
+ EXPECT_EQ(func(1, min), -static_cast<IntType>(integral_max + 1));
+ EXPECT_EQ(func(-1, max), -integral_max);
+ }
+ }
+
+ if constexpr (!has_integral) {
+ EXPECT_EQ(func(1, max), static_cast<IntType>(0));
+ EXPECT_EQ(func(2, max), static_cast<IntType>(1));
+
+ if constexpr (is_signed) {
+ EXPECT_EQ(func(1, min), static_cast<IntType>(-1));
+ EXPECT_EQ(func(-1, max), static_cast<IntType>(0));
+ EXPECT_EQ(func(-1, min), static_cast<IntType>(1));
+ }
+ }
+ }
+};
+
+#define LIST_MULIFX_TESTS(Name, IntType, FXType, func) \
+ using LlvmLibcMuli##Name##Test = MuliFxTest<IntType, FXType>; \
+ TEST_F(LlvmLibcMuli##Name##Test, BasicNumbers) { testBasicNumbers(&func); } \
+ TEST_F(LlvmLibcMuli##Name##Test, EdgeCases) { testEdgeCases(&func); } \
+ static_assert(true, "Require semicolon.")
diff --git a/libc/test/src/stdfix/mulik_test.cpp b/libc/test/src/stdfix/mulik_test.cpp
new file mode 100644
index 00000000000000..e8971c4ab6babc
--- /dev/null
+++ b/libc/test/src/stdfix/mulik_test.cpp
@@ -0,0 +1,14 @@
+//===-- Unittests for mulik -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MuliFxTest.h"
+
+#include "llvm-libc-macros/stdfix-macros.h"
+#include "src/stdfix/mulik.h"
+
+LIST_MULIFX_TESTS(k, int, accum, LIBC_NAMESPACE::mulik);
>From b94005cf411580c8f9a5d035d1145d5f2bca1da2 Mon Sep 17 00:00:00 2001
From: Jaeyong Lee <jay at glendalab.com>
Date: Mon, 26 Oct 2026 18:28:48 +0900
Subject: [PATCH 2/6] n == 0 early return is not worth it on multiply
---
libc/src/__support/fixed_point/fx_bits.h | 4 ----
1 file changed, 4 deletions(-)
diff --git a/libc/src/__support/fixed_point/fx_bits.h b/libc/src/__support/fixed_point/fx_bits.h
index 6e91092adfed3e..264cda957a504e 100644
--- a/libc/src/__support/fixed_point/fx_bits.h
+++ b/libc/src/__support/fixed_point/fx_bits.h
@@ -417,10 +417,6 @@ mulifx(IntType n, FXType m) {
static_assert(cpp::is_signed_v<IntType> == (FXRep::SIGN_LEN > 0),
"IntType and FXType must have matching signedness");
- if (LIBC_UNLIKELY(n == 0)) {
- return static_cast<IntType>(0);
- }
-
CompType m_comp = static_cast<CompType>(FXBits(m).get_bits());
constexpr int F = FXRep::FRACTION_LEN;
>From f8ce9c1a0c52eac10dfa5d5876511fff6b2a6830 Mon Sep 17 00:00:00 2001
From: Jaeyong Lee <jay at glendalab.com>
Date: Mon, 26 Oct 2026 18:38:13 +0900
Subject: [PATCH 3/6] Use a comparison for the mulifx rounding bias
---
libc/src/__support/fixed_point/fx_bits.h | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/libc/src/__support/fixed_point/fx_bits.h b/libc/src/__support/fixed_point/fx_bits.h
index 264cda957a504e..e3f034cf8d59e0 100644
--- a/libc/src/__support/fixed_point/fx_bits.h
+++ b/libc/src/__support/fixed_point/fx_bits.h
@@ -432,10 +432,9 @@ mulifx(IntType n, FXType m) {
WideType unscaled_result = n * static_cast<WideType>(m_comp);
if constexpr (FXRep::SIGN_LEN > 0) {
- constexpr WideType MASK = (WideType(1) << F) - 1;
- constexpr int SIGN_OFFSET = cpp::numeric_limits<WideType>::digits;
+ constexpr WideType BIAS = (WideType(1) << F) - 1;
WideType result =
- (unscaled_result + ((unscaled_result >> SIGN_OFFSET) & MASK)) >> F;
+ (unscaled_result + (unscaled_result < 0 ? BIAS : WideType(0))) >> F;
return static_cast<IntType>(result);
}
WideType result = unscaled_result >> F;
>From ba7afc976c948eabfe5b470fb1181069a6fb4327 Mon Sep 17 00:00:00 2001
From: Jaeyong Lee <jay at glendalab.com>
Date: Mon, 26 Oct 2026 18:45:45 +0900
Subject: [PATCH 4/6] static_cast n to WideType before multiplying
---
libc/src/__support/fixed_point/fx_bits.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libc/src/__support/fixed_point/fx_bits.h b/libc/src/__support/fixed_point/fx_bits.h
index e3f034cf8d59e0..639944e4aaae2c 100644
--- a/libc/src/__support/fixed_point/fx_bits.h
+++ b/libc/src/__support/fixed_point/fx_bits.h
@@ -429,7 +429,8 @@ mulifx(IntType n, FXType m) {
cpp::conditional_t<INTERMEDIATE_BITS <= 64, int64_t, Int128>,
cpp::conditional_t<INTERMEDIATE_BITS <= 64, uint64_t, UInt128>>;
- WideType unscaled_result = n * static_cast<WideType>(m_comp);
+ WideType unscaled_result =
+ static_cast<WideType>(n) * static_cast<WideType>(m_comp);
if constexpr (FXRep::SIGN_LEN > 0) {
constexpr WideType BIAS = (WideType(1) << F) - 1;
>From e790eab24845c1691568836bd06b8470c538686b Mon Sep 17 00:00:00 2001
From: Jaeyong Lee <jay at glendalab.com>
Date: Mon, 26 Oct 2026 21:36:51 +0900
Subject: [PATCH 5/6] Use cpp::numeric_limits<T>::digits for WideType condition
---
libc/src/__support/fixed_point/fx_bits.h | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/libc/src/__support/fixed_point/fx_bits.h b/libc/src/__support/fixed_point/fx_bits.h
index 639944e4aaae2c..f9de1952ff08b5 100644
--- a/libc/src/__support/fixed_point/fx_bits.h
+++ b/libc/src/__support/fixed_point/fx_bits.h
@@ -426,8 +426,12 @@ mulifx(IntType n, FXType m) {
using WideType = cpp::conditional_t<
cpp::is_signed_v<IntType>,
- cpp::conditional_t<INTERMEDIATE_BITS <= 64, int64_t, Int128>,
- cpp::conditional_t<INTERMEDIATE_BITS <= 64, uint64_t, UInt128>>;
+ cpp::conditional_t<INTERMEDIATE_BITS <=
+ cpp::numeric_limits<int64_t>::digits,
+ int64_t, Int128>,
+ cpp::conditional_t<INTERMEDIATE_BITS <=
+ cpp::numeric_limits<uint64_t>::digits,
+ uint64_t, UInt128>>;
WideType unscaled_result =
static_cast<WideType>(n) * static_cast<WideType>(m_comp);
>From 783c9e8098f38897f0d87bb9a58947a1a053fc07 Mon Sep 17 00:00:00 2001
From: Jaeyong Lee <jay at glendalab.com>
Date: Mon, 26 Oct 2026 22:45:56 +0900
Subject: [PATCH 6/6] Use cpp::numeric_limits<IntType>::digits for counting
INTERMEDIATE_BITS
---
libc/src/__support/fixed_point/fx_bits.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libc/src/__support/fixed_point/fx_bits.h b/libc/src/__support/fixed_point/fx_bits.h
index f9de1952ff08b5..0037f000915fc9 100644
--- a/libc/src/__support/fixed_point/fx_bits.h
+++ b/libc/src/__support/fixed_point/fx_bits.h
@@ -422,7 +422,7 @@ mulifx(IntType n, FXType m) {
constexpr int F = FXRep::FRACTION_LEN;
constexpr int INTERMEDIATE_BITS =
- sizeof(IntType) * 8 - cpp::is_signed_v<IntType> + FXRep::VALUE_LEN;
+ cpp::numeric_limits<IntType>::digits + FXRep::VALUE_LEN;
using WideType = cpp::conditional_t<
cpp::is_signed_v<IntType>,
More information about the libc-commits
mailing list