[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