[libc-commits] [libc] [libc][stdfix] Implement fixed point fxdivi functions in llvm-libc (PR #210020)

via libc-commits libc-commits at lists.llvm.org
Fri Jul 24 03:17:17 PDT 2026


https://github.com/sohail103 updated https://github.com/llvm/llvm-project/pull/210020

>From ffdb1ddc750cf2b6d03144e2efe34841bcb9b81a Mon Sep 17 00:00:00 2001
From: sohail103 <sohailraj.satapathy at gmail.com>
Date: Thu, 16 Jul 2026 10:19:24 +0530
Subject: [PATCH 1/2] [libc][stdfix] Implement fixed point fxdivi functions in
 llvm-libc

---
 libc/config/baremetal/arm/entrypoints.txt   |   8 +
 libc/config/baremetal/riscv/entrypoints.txt |   8 +
 libc/config/linux/riscv/entrypoints.txt     |   7 +
 libc/config/linux/x86_64/entrypoints.txt    |   7 +
 libc/docs/headers/stdfix.rst                |   2 +-
 libc/include/stdfix.yaml                    |  56 ++++++
 libc/src/__support/fixed_point/fx_bits.h    | 200 +++++++++++---------
 libc/src/stdfix/CMakeLists.txt              |   2 +-
 libc/src/stdfix/kdivi.cpp                   |  19 ++
 libc/src/stdfix/kdivi.h                     |  21 ++
 libc/src/stdfix/lkdivi.cpp                  |  19 ++
 libc/src/stdfix/lkdivi.h                    |  21 ++
 libc/src/stdfix/lrdivi.cpp                  |  19 ++
 libc/src/stdfix/lrdivi.h                    |  21 ++
 libc/src/stdfix/rdivi.cpp                   |  12 +-
 libc/src/stdfix/rdivi.h                     |   2 +-
 libc/src/stdfix/ukdivi.cpp                  |  19 ++
 libc/src/stdfix/ukdivi.h                    |  21 ++
 libc/src/stdfix/ulkdivi.cpp                 |  20 ++
 libc/src/stdfix/ulkdivi.h                   |  21 ++
 libc/src/stdfix/ulrdivi.cpp                 |  20 ++
 libc/src/stdfix/ulrdivi.h                   |  21 ++
 libc/src/stdfix/urdivi.cpp                  |  19 ++
 libc/src/stdfix/urdivi.h                    |  21 ++
 libc/test/src/stdfix/CMakeLists.txt         |   4 +-
 libc/test/src/stdfix/DivITest.h             |  74 --------
 libc/test/src/stdfix/FxDiviTest.h           | 189 ++++++++++++++++++
 libc/test/src/stdfix/kdivi_test.cpp         |  14 ++
 libc/test/src/stdfix/lkdivi_test.cpp        |  14 ++
 libc/test/src/stdfix/lrdivi_test.cpp        |  14 ++
 libc/test/src/stdfix/rdivi_test.cpp         |   6 +-
 libc/test/src/stdfix/ukdivi_test.cpp        |  14 ++
 libc/test/src/stdfix/ulkdivi_test.cpp       |  15 ++
 libc/test/src/stdfix/ulrdivi_test.cpp       |  15 ++
 libc/test/src/stdfix/urdivi_test.cpp        |  14 ++
 35 files changed, 781 insertions(+), 178 deletions(-)
 create mode 100644 libc/src/stdfix/kdivi.cpp
 create mode 100644 libc/src/stdfix/kdivi.h
 create mode 100644 libc/src/stdfix/lkdivi.cpp
 create mode 100644 libc/src/stdfix/lkdivi.h
 create mode 100644 libc/src/stdfix/lrdivi.cpp
 create mode 100644 libc/src/stdfix/lrdivi.h
 create mode 100644 libc/src/stdfix/ukdivi.cpp
 create mode 100644 libc/src/stdfix/ukdivi.h
 create mode 100644 libc/src/stdfix/ulkdivi.cpp
 create mode 100644 libc/src/stdfix/ulkdivi.h
 create mode 100644 libc/src/stdfix/ulrdivi.cpp
 create mode 100644 libc/src/stdfix/ulrdivi.h
 create mode 100644 libc/src/stdfix/urdivi.cpp
 create mode 100644 libc/src/stdfix/urdivi.h
 delete mode 100644 libc/test/src/stdfix/DivITest.h
 create mode 100644 libc/test/src/stdfix/FxDiviTest.h
 create mode 100644 libc/test/src/stdfix/kdivi_test.cpp
 create mode 100644 libc/test/src/stdfix/lkdivi_test.cpp
 create mode 100644 libc/test/src/stdfix/lrdivi_test.cpp
 create mode 100644 libc/test/src/stdfix/ukdivi_test.cpp
 create mode 100644 libc/test/src/stdfix/ulkdivi_test.cpp
 create mode 100644 libc/test/src/stdfix/ulrdivi_test.cpp
 create mode 100644 libc/test/src/stdfix/urdivi_test.cpp

diff --git a/libc/config/baremetal/arm/entrypoints.txt b/libc/config/baremetal/arm/entrypoints.txt
index 71ecbe438bf7b..5f117db07f526 100644
--- a/libc/config/baremetal/arm/entrypoints.txt
+++ b/libc/config/baremetal/arm/entrypoints.txt
@@ -983,6 +983,14 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
     libc.src.stdfix.diviulr
     libc.src.stdfix.diviuk
     libc.src.stdfix.diviulk
+    libc.src.stdfix.rdivi
+    libc.src.stdfix.lrdivi
+    libc.src.stdfix.kdivi
+    libc.src.stdfix.lkdivi
+    libc.src.stdfix.urdivi
+    libc.src.stdfix.ulrdivi
+    libc.src.stdfix.ukdivi
+    libc.src.stdfix.ulkdivi
   )
 endif()
 
diff --git a/libc/config/baremetal/riscv/entrypoints.txt b/libc/config/baremetal/riscv/entrypoints.txt
index b38f60ab9d5f5..3e92bc17cbb2b 100644
--- a/libc/config/baremetal/riscv/entrypoints.txt
+++ b/libc/config/baremetal/riscv/entrypoints.txt
@@ -980,6 +980,14 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
     libc.src.stdfix.diviulr
     libc.src.stdfix.diviuk
     libc.src.stdfix.diviulk
+    libc.src.stdfix.rdivi
+    libc.src.stdfix.lrdivi
+    libc.src.stdfix.kdivi
+    libc.src.stdfix.lkdivi
+    libc.src.stdfix.urdivi
+    libc.src.stdfix.ulrdivi
+    libc.src.stdfix.ukdivi
+    libc.src.stdfix.ulkdivi
   )
 endif()
 
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index c6a4b431f33de..0dc4693327a03 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1190,6 +1190,13 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
     libc.src.stdfix.idivuk
     libc.src.stdfix.idivulk
     libc.src.stdfix.rdivi
+    libc.src.stdfix.lrdivi
+    libc.src.stdfix.kdivi
+    libc.src.stdfix.lkdivi
+    libc.src.stdfix.urdivi
+    libc.src.stdfix.ulrdivi
+    libc.src.stdfix.ukdivi
+    libc.src.stdfix.ulkdivi
     libc.src.stdfix.divir
     libc.src.stdfix.divilr
     libc.src.stdfix.divik
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index fb0886e2e650a..0023c8ca8dd46 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -1195,6 +1195,13 @@ if(LIBC_COMPILER_HAS_FIXED_POINT)
     libc.src.stdfix.idivuk
     libc.src.stdfix.idivulk
     libc.src.stdfix.rdivi
+    libc.src.stdfix.lrdivi
+    libc.src.stdfix.kdivi
+    libc.src.stdfix.lkdivi
+    libc.src.stdfix.urdivi
+    libc.src.stdfix.ulrdivi
+    libc.src.stdfix.ukdivi
+    libc.src.stdfix.ulkdivi
     libc.src.stdfix.divir
     libc.src.stdfix.divilr
     libc.src.stdfix.divik
diff --git a/libc/docs/headers/stdfix.rst b/libc/docs/headers/stdfix.rst
index 91dad81c595b0..ac1419be7a066 100644
--- a/libc/docs/headers/stdfix.rst
+++ b/libc/docs/headers/stdfix.rst
@@ -79,7 +79,7 @@ The following functions are included in the ISO/IEC TR 18037:2008 standard.
 +---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
 | muli          |                |             |               |            |                |             |                |             |               |            |                |             |
 +---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
-| \*divi        |                |             |               | |check|    |                |             |                |             |               |            |                |             |
+| \*divi        |                |             | |check|       | |check|    | |check|        | |check|     |                |             | |check|       | |check|    | |check|        | |check|     |
 +---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
 | round         | |check|        | |check|     | |check|       | |check|    | |check|        | |check|     | |check|        | |check|     | |check|       | |check|    | |check|        | |check|     |
 +---------------+----------------+-------------+---------------+------------+----------------+-------------+----------------+-------------+---------------+------------+----------------+-------------+
diff --git a/libc/include/stdfix.yaml b/libc/include/stdfix.yaml
index f91e4ea620e3e..739805ea2a39a 100644
--- a/libc/include/stdfix.yaml
+++ b/libc/include/stdfix.yaml
@@ -565,6 +565,62 @@ functions:
       - type: int
       - type: int
     guard: LIBC_COMPILER_HAS_FIXED_POINT
+  - name: lrdivi
+    standards:
+      - stdc_ext
+    return_type: long fract
+    arguments:
+      - type: long int
+      - type: long int
+    guard: LIBC_COMPILER_HAS_FIXED_POINT
+  - name: kdivi
+    standards:
+      - stdc_ext
+    return_type: accum
+    arguments:
+      - type: int
+      - type: int
+    guard: LIBC_COMPILER_HAS_FIXED_POINT
+  - name: lkdivi
+    standards:
+      - stdc_ext
+    return_type: long accum
+    arguments:
+      - type: long int
+      - type: long int
+    guard: LIBC_COMPILER_HAS_FIXED_POINT
+  - name: urdivi
+    standards:
+      - stdc_ext
+    return_type: unsigned fract
+    arguments:
+      - type: unsigned int
+      - type: unsigned int
+    guard: LIBC_COMPILER_HAS_FIXED_POINT
+  - name: ulrdivi
+    standards:
+      - stdc_ext
+    return_type: unsigned long fract
+    arguments:
+      - type: unsigned long int
+      - type: unsigned long int
+    guard: LIBC_COMPILER_HAS_FIXED_POINT
+  - name: ukdivi
+    standards:
+      - stdc_ext
+    return_type: unsigned accum
+    arguments:
+      - type: unsigned int
+      - type: unsigned int
+    guard: LIBC_COMPILER_HAS_FIXED_POINT
+  - name: ulkdivi
+    standards:
+      - stdc_ext
+    return_type: unsigned long accum
+    arguments:
+      - type: unsigned long int
+      - type: unsigned long int
+    guard: LIBC_COMPILER_HAS_FIXED_POINT
   - name: divir
     standards:
       - stdc_ext
diff --git a/libc/src/__support/fixed_point/fx_bits.h b/libc/src/__support/fixed_point/fx_bits.h
index 256bb3a967fbf..6b800372d316b 100644
--- a/libc/src/__support/fixed_point/fx_bits.h
+++ b/libc/src/__support/fixed_point/fx_bits.h
@@ -227,111 +227,133 @@ idiv(T x, T y) {
   return static_cast<XType>(result);
 }
 
-LIBC_INLINE long accum nrstep(long accum d, long accum x0) {
-  auto v = x0 * (2.lk - (d * x0));
-  return v;
-}
-
-// Divide the two integers and return a fixed_point value
-//
+// Divide two integers and return a fixed-point value.
 // For reference, see:
 // https://en.wikipedia.org/wiki/Division_algorithm#Newton%E2%80%93Raphson_division
-// https://stackoverflow.com/a/9231996
+// https://stackoverflow.com/a/9231996.
+template <typename FXType, typename IntType>
+LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_fixed_point_v<FXType>, FXType>
+fxdivi(IntType n, IntType d) {
+  using OutRep = FXRep<FXType>;
+  static_assert(cpp::is_signed_v<IntType> == (OutRep::SIGN_LEN > 0),
+                "IntType and FXType must have matching signedness");
+  constexpr bool IS_SIGNED = OutRep::SIGN_LEN > 0;
+  using UIntType = cpp::make_unsigned_t<IntType>;
 
-template <typename XType> LIBC_INLINE constexpr XType divi(int n, int d) {
   // If the value of the second operand of the / operator is zero, the
-  // behavior is undefined. Ref: ISO/IEC TR 18037:2008(E) p.g. 16
+  // behavior is undefined. Ref: ISO/IEC TR 18037:2008(E) p.g. 16.
   LIBC_CRASH_ON_VALUE(d, 0);
-
-  if (LIBC_UNLIKELY(n == 0)) {
-    return FXRep<XType>::ZERO();
+  if (LIBC_UNLIKELY(n == 0))
+    return OutRep::ZERO();
+
+  // n == d and d != 0 means the quotient is 1. The general NR path can't
+  // guarantee landing on 1 exactly so special case for this.
+  if (LIBC_UNLIKELY(n == d)) {
+    if constexpr (OutRep::INTEGRAL_LEN > 0)
+      return static_cast<FXType>(1);
+    else
+      return OutRep::MAX();
   }
-  auto is_power_of_two = [](int n) { return (n > 0) && ((n & (n - 1)) == 0); };
-  long accum max_val = static_cast<long accum>(FXRep<XType>::MAX());
-  long accum min_val = static_cast<long accum>(FXRep<XType>::MIN());
-
-  if (is_power_of_two(cpp::abs(d))) {
-    int k = cpp::countr_zero<uint32_t>(static_cast<uint32_t>(cpp::abs(d)));
-    constexpr int F = FXRep<XType>::FRACTION_LEN;
-    int64_t scaled_n = static_cast<int64_t>(n) << F;
-    int64_t res64 = scaled_n >> k;
-    constexpr int TOTAL_BITS = sizeof(XType) * 8;
-    const int64_t max_limit = (1LL << (TOTAL_BITS - 1)) - 1;
-    const int64_t min_limit = -(1LL << (TOTAL_BITS - 1));
-    if (res64 > max_limit) {
-      return FXRep<XType>::MAX();
-    } else if (res64 < min_limit) {
-      return FXRep<XType>::MIN();
-    }
-    long accum res_accum =
-        static_cast<long accum>(res64) / static_cast<long accum>(1 << F);
-    res_accum = (d < 0) ? static_cast<long accum>(-1) * res_accum : res_accum;
-    if (res_accum > max_val) {
-      return FXRep<XType>::MAX();
-    } else if (res_accum < min_val) {
-      return FXRep<XType>::MIN();
-    }
-    return static_cast<XType>(res_accum);
+
+  // Intermediate arithmetic is done in a wide fixed-point type.
+  using WideFXType =
+      cpp::conditional_t<IS_SIGNED, long accum, unsigned long accum>;
+  using WideRep = FXRep<WideFXType>;
+  using WideStorage = typename WideRep::StorageType;
+  constexpr int F = OutRep::FRACTION_LEN;
+  constexpr int WF = WideRep::FRACTION_LEN;
+
+  // Split each operand into a sign and a magnitude.
+  bool result_is_negative = false;
+  UIntType n_mag, d_mag;
+  if constexpr (IS_SIGNED) {
+    result_is_negative = (n < 0) != (d < 0);
+    n_mag = (n < 0) ? -static_cast<UIntType>(n) : static_cast<UIntType>(n);
+    d_mag = (d < 0) ? -static_cast<UIntType>(d) : static_cast<UIntType>(d);
+  } else {
+    n_mag = n;
+    d_mag = d;
   }
 
-  bool result_is_negative = ((n < 0) != (d < 0));
-  int64_t n64 = static_cast<int64_t>(n);
-  int64_t d64 = static_cast<int64_t>(d);
+  WideFXType res;
 
-  uint64_t nv = static_cast<uint64_t>(n64 < 0 ? -n64 : n64);
-  uint64_t dv = static_cast<uint64_t>(d64 < 0 ? -d64 : d64);
+  if ((d_mag & (d_mag - 1)) == 0) {
+    // d is a power of 2. n/d is an exact right shift.
+    int log2_d = cpp::countr_zero(d_mag);
 
-  if (d == INT_MIN) {
-    nv <<= 1;
-    dv >>= 1;
-  }
+    constexpr int INTERMEDIATE_BITS =
+        cpp::numeric_limits<UIntType>::digits + WF;
+    using WideIntType =
+        cpp::conditional_t<(INTERMEDIATE_BITS <= 64), uint64_t, UInt128>;
+    WideIntType scaled_n = (static_cast<WideIntType>(n_mag) << WF) >> log2_d;
 
-  uint32_t clz = cpp::countl_zero<uint32_t>(static_cast<uint32_t>(dv)) - 1;
-  uint64_t scaled_val = dv << clz;
-  // Scale denominator to be in the range of [0.5,1]
-  FXBits<long accum> d_scaled{scaled_val};
-  uint64_t scaled_val_n = nv << clz;
-  // Scale the numerator as much as the denominator to maintain correctness of
-  // the original equation
-  FXBits<long accum> n_scaled{scaled_val_n};
-  long accum n_scaled_val = n_scaled.get_val();
-  long accum d_scaled_val = d_scaled.get_val();
-  // x0 = (48/17) - (32/17) * d_n
-  long accum a = 0x2.d89d89d8p0lk; // 48/17 = 2.8235294...
-  long accum b = 0x1.e1e1e1e1p0lk; // 32/17 = 1.8823529...
-  // Error of the initial approximation, as derived
-  // from the wikipedia article is
-  //  E0 = 1/17 = 0.059 (5.9%)
-  long accum initial_approx = a - (b * d_scaled_val);
-  // Since, 0.5 <= d_scaled_val <= 1.0, 0.9412 <= initial_approx <= 1.88235
-  LIBC_ASSERT((initial_approx >= 0x0.78793dd9p0lk) &&
-              (initial_approx <= 0x1.f0f0d845p0lk));
-  // Each newton-raphson iteration will square the error, due
-  // to quadratic convergence. So,
-  // E1 = (0.059)^2 = 0.0034
-  long accum val = nrstep(d_scaled_val, initial_approx);
-  if constexpr (FXRep<XType>::FRACTION_LEN > 8) {
-    // E2 = 0.0000121
-    val = nrstep(d_scaled_val, val);
-    if constexpr (FXRep<XType>::FRACTION_LEN > 16) {
-      // E3 = 1.468e−10
-      val = nrstep(d_scaled_val, val);
-    }
+    constexpr int WIDE_STORAGE_BITS = cpp::numeric_limits<WideStorage>::digits;
+    if (LIBC_UNLIKELY((static_cast<UInt128>(scaled_n) >> WIDE_STORAGE_BITS) !=
+                      0))
+      return result_is_negative ? OutRep::MIN() : OutRep::MAX();
+
+    res = FXBits<WideFXType>(static_cast<WideStorage>(scaled_n)).get_val();
+  } else {
+    // General case: Approximate 1/d, then multiply by n.
+
+    // Normalize d_mag into a WF fraction value in [0.5, 1) and apply the same
+    // shift to n_mag so that n_scaled/d_scaled = n/d.
+    constexpr int W = cpp::numeric_limits<UIntType>::digits;
+    int d_msb = (W - 1) - cpp::countl_zero(d_mag);
+    int norm_shift = (WF - 1) - d_msb;
+
+    auto scale = [norm_shift](UIntType v) -> WideFXType {
+      WideStorage wide_v = static_cast<WideStorage>(v);
+      WideStorage shifted =
+          norm_shift >= 0 ? (wide_v << norm_shift) : (wide_v >> -norm_shift);
+      return FXBits<WideFXType>(shifted).get_val();
+    };
+
+    WideFXType d_scaled = scale(d_mag);
+    WideFXType n_scaled = scale(n_mag);
+
+    // Initial approximation of 1/d_scaled: x0 = 48/17 - (32/17) * d_scaled.
+    // d_scaled is in [0.5, 1) so x0 is in [0.941, 1.882] with a worst-case
+    // relative error bounded by 1/17 (~5.88%).
+    WideFXType a = static_cast<WideFXType>(0x2.d89d89d8p0lk); // 48/17
+    WideFXType b = static_cast<WideFXType>(0x1.e1e1e1e1p0lk); // 32/17
+    WideFXType initial_approx = a - b * d_scaled;
+
+    auto nrstep = [](WideFXType d_, WideFXType x0) {
+      return x0 * (static_cast<WideFXType>(2) - d_ * x0);
+    };
+
+    // Each iteration squares the relative error (quadratic convergence).
+    WideFXType recip = nrstep(d_scaled, initial_approx); // E1 <= 0.346%
+
+    if constexpr (F >= 7)
+      recip = nrstep(d_scaled, recip); // E2 <= 1.197e-5
+
+    if constexpr (F >= 15)
+      recip = nrstep(d_scaled, recip); // E3 <= 1.434e-10
+
+    if constexpr (F >= 31)
+      recip = nrstep(d_scaled, recip); // E4 <= 2.055e-20
+
+    res = n_scaled * recip;
   }
-  long accum res = n_scaled_val * val;
 
-  if (result_is_negative) {
-    res *= static_cast<long accum>(-1);
+  if constexpr (IS_SIGNED) {
+    if (result_is_negative)
+      res = -res;
   }
 
-  // Per clause 7.18a.6.1, saturate values on overflow
-  if (res > max_val) {
-    return FXRep<XType>::MAX();
-  } else if (res < min_val) {
-    return FXRep<XType>::MIN();
-  } else {
-    return static_cast<XType>(res);
+  // According to clause 7.18a.6.1, saturate the result on overflow.
+  WideFXType max_val = static_cast<WideFXType>(OutRep::MAX());
+  if (res > max_val)
+    return OutRep::MAX();
+  if constexpr (IS_SIGNED) {
+    WideFXType min_val = static_cast<WideFXType>(OutRep::MIN());
+    if (res < min_val)
+      return OutRep::MIN();
   }
+
+  return static_cast<FXType>(res);
 }
 
 // Divide an integer operand by a fixed-point operand and return the
diff --git a/libc/src/stdfix/CMakeLists.txt b/libc/src/stdfix/CMakeLists.txt
index b7121407e0307..71ad93c321e69 100644
--- a/libc/src/stdfix/CMakeLists.txt
+++ b/libc/src/stdfix/CMakeLists.txt
@@ -88,7 +88,7 @@ foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk)
   )
 endforeach()
 
-foreach(suffix IN ITEMS r)
+foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk)
   add_entrypoint_object(
     ${suffix}divi
     HDRS
diff --git a/libc/src/stdfix/kdivi.cpp b/libc/src/stdfix/kdivi.cpp
new file mode 100644
index 0000000000000..0441c495ffada
--- /dev/null
+++ b/libc/src/stdfix/kdivi.cpp
@@ -0,0 +1,19 @@
+//===-- Implementation of kdivi 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 "kdivi.h"
+#include "src/__support/common.h"
+#include "src/__support/fixed_point/fx_bits.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(accum, kdivi, (int n, int d)) {
+  return fixed_point::fxdivi<accum, int>(n, d);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/kdivi.h b/libc/src/stdfix/kdivi.h
new file mode 100644
index 0000000000000..b570b5c213fd6
--- /dev/null
+++ b/libc/src/stdfix/kdivi.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for kdivi -------------------------*- 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_KDIVI_H
+#define LLVM_LIBC_SRC_STDFIX_KDIVI_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+accum kdivi(int n, int d);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_KDIVI_H
diff --git a/libc/src/stdfix/lkdivi.cpp b/libc/src/stdfix/lkdivi.cpp
new file mode 100644
index 0000000000000..82755ddfe8d38
--- /dev/null
+++ b/libc/src/stdfix/lkdivi.cpp
@@ -0,0 +1,19 @@
+//===-- Implementation of lkdivi 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 "lkdivi.h"
+#include "src/__support/common.h"
+#include "src/__support/fixed_point/fx_bits.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(long accum, lkdivi, (long int n, long int d)) {
+  return fixed_point::fxdivi<long accum, long int>(n, d);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/lkdivi.h b/libc/src/stdfix/lkdivi.h
new file mode 100644
index 0000000000000..353ac39974622
--- /dev/null
+++ b/libc/src/stdfix/lkdivi.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for lkdivi ------------------------*- 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_LKDIVI_H
+#define LLVM_LIBC_SRC_STDFIX_LKDIVI_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+long accum lkdivi(long int n, long int d);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_LKDIVI_H
diff --git a/libc/src/stdfix/lrdivi.cpp b/libc/src/stdfix/lrdivi.cpp
new file mode 100644
index 0000000000000..31ff469835829
--- /dev/null
+++ b/libc/src/stdfix/lrdivi.cpp
@@ -0,0 +1,19 @@
+//===-- Implementation of lrdivi 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 "lrdivi.h"
+#include "src/__support/common.h"
+#include "src/__support/fixed_point/fx_bits.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(long fract, lrdivi, (long int n, long int d)) {
+  return fixed_point::fxdivi<long fract, long int>(n, d);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/lrdivi.h b/libc/src/stdfix/lrdivi.h
new file mode 100644
index 0000000000000..9f17ac8b5667c
--- /dev/null
+++ b/libc/src/stdfix/lrdivi.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for lrdivi ------------------------*- 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_LRDIVI_H
+#define LLVM_LIBC_SRC_STDFIX_LRDIVI_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+long fract lrdivi(long int n, long int d);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_LRDIVI_H
diff --git a/libc/src/stdfix/rdivi.cpp b/libc/src/stdfix/rdivi.cpp
index 7021a8b1c8b2a..863f40c060cd7 100644
--- a/libc/src/stdfix/rdivi.cpp
+++ b/libc/src/stdfix/rdivi.cpp
@@ -1,4 +1,4 @@
-//===-- Implementation of rdivi function ---------------------------------===//
+//===-- Implementation of rdivi function ----------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -7,15 +7,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "rdivi.h"
-#include "include/llvm-libc-macros/stdfix-macros.h" // fract
-#include "src/__support/common.h"                   // LLVM_LIBC_FUNCTION
-#include "src/__support/fixed_point/fx_bits.h"      // fixed_point
-#include "src/__support/macros/config.h"            // LIBC_NAMESPACE_DECL
+#include "src/__support/common.h"
+#include "src/__support/fixed_point/fx_bits.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
-LLVM_LIBC_FUNCTION(fract, rdivi, (int a, int b)) {
-  return fixed_point::divi<fract>(a, b);
+LLVM_LIBC_FUNCTION(fract, rdivi, (int n, int d)) {
+  return fixed_point::fxdivi<fract, int>(n, d);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/rdivi.h b/libc/src/stdfix/rdivi.h
index aeda1ee9d40f0..5e9aa25dea74d 100644
--- a/libc/src/stdfix/rdivi.h
+++ b/libc/src/stdfix/rdivi.h
@@ -14,7 +14,7 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-fract rdivi(int a, int b);
+fract rdivi(int n, int d);
 
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/src/stdfix/ukdivi.cpp b/libc/src/stdfix/ukdivi.cpp
new file mode 100644
index 0000000000000..bd41226c45152
--- /dev/null
+++ b/libc/src/stdfix/ukdivi.cpp
@@ -0,0 +1,19 @@
+//===-- Implementation of ukdivi 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 "ukdivi.h"
+#include "src/__support/common.h"
+#include "src/__support/fixed_point/fx_bits.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(unsigned accum, ukdivi, (unsigned int n, unsigned int d)) {
+  return fixed_point::fxdivi<unsigned accum, unsigned int>(n, d);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/ukdivi.h b/libc/src/stdfix/ukdivi.h
new file mode 100644
index 0000000000000..5c28ff717336f
--- /dev/null
+++ b/libc/src/stdfix/ukdivi.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for ukdivi ------------------------*- 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_UKDIVI_H
+#define LLVM_LIBC_SRC_STDFIX_UKDIVI_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+unsigned accum ukdivi(unsigned int n, unsigned int d);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_UKDIVI_H
diff --git a/libc/src/stdfix/ulkdivi.cpp b/libc/src/stdfix/ulkdivi.cpp
new file mode 100644
index 0000000000000..f3232dbe3ca3f
--- /dev/null
+++ b/libc/src/stdfix/ulkdivi.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of ulkdivi 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 "ulkdivi.h"
+#include "src/__support/common.h"
+#include "src/__support/fixed_point/fx_bits.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(unsigned long accum, ulkdivi,
+                   (unsigned long int n, unsigned long int d)) {
+  return fixed_point::fxdivi<unsigned long accum, unsigned long int>(n, d);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/ulkdivi.h b/libc/src/stdfix/ulkdivi.h
new file mode 100644
index 0000000000000..1cb03c7bdf771
--- /dev/null
+++ b/libc/src/stdfix/ulkdivi.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for ulkdivi -----------------------*- 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_ULKDIVI_H
+#define LLVM_LIBC_SRC_STDFIX_ULKDIVI_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+unsigned long accum ulkdivi(unsigned long int n, unsigned long int d);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_ULKDIVI_H
diff --git a/libc/src/stdfix/ulrdivi.cpp b/libc/src/stdfix/ulrdivi.cpp
new file mode 100644
index 0000000000000..db73eb1bbabeb
--- /dev/null
+++ b/libc/src/stdfix/ulrdivi.cpp
@@ -0,0 +1,20 @@
+//===-- Implementation of ulrdivi 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 "ulrdivi.h"
+#include "src/__support/common.h"
+#include "src/__support/fixed_point/fx_bits.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(unsigned long fract, ulrdivi,
+                   (unsigned long int n, unsigned long int d)) {
+  return fixed_point::fxdivi<unsigned long fract, unsigned long int>(n, d);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/ulrdivi.h b/libc/src/stdfix/ulrdivi.h
new file mode 100644
index 0000000000000..8f32f87a9c171
--- /dev/null
+++ b/libc/src/stdfix/ulrdivi.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for ulrdivi -----------------------*- 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_ULRDIVI_H
+#define LLVM_LIBC_SRC_STDFIX_ULRDIVI_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+unsigned long fract ulrdivi(unsigned long int n, unsigned long int d);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_ULRDIVI_H
diff --git a/libc/src/stdfix/urdivi.cpp b/libc/src/stdfix/urdivi.cpp
new file mode 100644
index 0000000000000..104eb17fda1d2
--- /dev/null
+++ b/libc/src/stdfix/urdivi.cpp
@@ -0,0 +1,19 @@
+//===-- Implementation of urdivi 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 "urdivi.h"
+#include "src/__support/common.h"
+#include "src/__support/fixed_point/fx_bits.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(unsigned fract, urdivi, (unsigned int n, unsigned int d)) {
+  return fixed_point::fxdivi<unsigned fract, unsigned int>(n, d);
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdfix/urdivi.h b/libc/src/stdfix/urdivi.h
new file mode 100644
index 0000000000000..51c4f5fe7be8b
--- /dev/null
+++ b/libc/src/stdfix/urdivi.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for urdivi ------------------------*- 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_URDIVI_H
+#define LLVM_LIBC_SRC_STDFIX_URDIVI_H
+
+#include "include/llvm-libc-macros/stdfix-macros.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+unsigned fract urdivi(unsigned int n, unsigned int d);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDFIX_URDIVI_H
diff --git a/libc/test/src/stdfix/CMakeLists.txt b/libc/test/src/stdfix/CMakeLists.txt
index 8ebf69c66d66c..ea5b3edc636e6 100644
--- a/libc/test/src/stdfix/CMakeLists.txt
+++ b/libc/test/src/stdfix/CMakeLists.txt
@@ -131,13 +131,13 @@ foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk)
   )
 endforeach()
 
-foreach(suffix IN ITEMS r)
+foreach(suffix IN ITEMS r lr k lk ur ulr uk ulk)
   add_libc_test(
     ${suffix}divi_test
     SUITE
       libc-stdfix-tests
     HDRS
-      DivITest.h
+      FxDiviTest.h
     SRCS
       ${suffix}divi_test.cpp
     DEPENDS
diff --git a/libc/test/src/stdfix/DivITest.h b/libc/test/src/stdfix/DivITest.h
deleted file mode 100644
index ad15637a3455f..0000000000000
--- a/libc/test/src/stdfix/DivITest.h
+++ /dev/null
@@ -1,74 +0,0 @@
-//===-- Utility class to test fxdivi 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 "src/__support/CPP/type_traits.h"
-#include "src/__support/fixed_point/fx_bits.h"
-#include "src/__support/fixed_point/fx_rep.h"
-#include "test/UnitTest/Test.h"
-
-template <typename XType> XType get_epsilon() = delete;
-template <> fract get_epsilon() { return FRACT_EPSILON; }
-template <> unsigned fract get_epsilon() { return UFRACT_EPSILON; }
-template <> long fract get_epsilon() { return LFRACT_EPSILON; }
-
-template <typename XType>
-class DivITest : public LIBC_NAMESPACE::testing::Test {
-  using FXRep = LIBC_NAMESPACE::fixed_point::FXRep<XType>;
-  using FXBits = LIBC_NAMESPACE::fixed_point::FXBits<XType>;
-
-public:
-  typedef XType (*DivIFunc)(int, int);
-
-  void testBasic(DivIFunc func) {
-    XType epsilon = get_epsilon<XType>();
-    EXPECT_LT((func(2, 3) - 0.666656494140625r), epsilon);
-    EXPECT_LT((func(3, 4) - 0.75r), epsilon);
-    EXPECT_LT((func(1043, 2764) - 0.3773516643r), epsilon);
-    EXPECT_LT((func(60000, 720293) - 0.08329943509r), epsilon);
-
-    EXPECT_EQ(func(128, 256), 0.5r);
-    EXPECT_EQ(func(1, 2), 0.5r);
-    EXPECT_EQ(func(1, 4), 0.25r);
-    EXPECT_EQ(func(1, 8), 0.125r);
-    EXPECT_EQ(func(1, 16), 0.0625r);
-
-    EXPECT_EQ(func(-1, 2), -0.5r);
-    EXPECT_EQ(func(1, -4), -0.25r);
-    EXPECT_EQ(func(-1, 8), -0.125r);
-    EXPECT_EQ(func(1, -16), -0.0625r);
-  }
-
-  void testSpecial(DivIFunc func) {
-    XType epsilon = get_epsilon<XType>();
-    EXPECT_EQ(func(0, 10), 0.r);
-    EXPECT_EQ(func(0, -10), 0.r);
-    EXPECT_EQ(func(-(1 << FRACT_FBIT), 1 << FRACT_FBIT), FRACT_MIN);
-    EXPECT_EQ(func((1 << FRACT_FBIT) - 1, 1 << FRACT_FBIT), FRACT_MAX);
-    // From Section 7.18a.6.1, functions returning a fixed-point value, the
-    // return value is saturated on overflow.
-    EXPECT_EQ(func(INT_MAX, INT_MAX), FRACT_MAX);
-    EXPECT_LT(func(INT_MAX - 1, INT_MAX) - 0.99999999r, epsilon);
-    EXPECT_EQ(func(INT_MIN, INT_MAX), FRACT_MIN);
-    // Expecting 0 here as fract is not precise enough to
-    // handle 1/INT_MAX
-    EXPECT_LT(func(1, INT_MAX) - 0.r, epsilon);
-    // This results in 1.1739, which should be saturated to FRACT_MAX
-    EXPECT_EQ(func(27, 23), FRACT_MAX);
-
-    EXPECT_EQ(func(INT_MIN, 1), FRACT_MIN);
-    EXPECT_LT(func(1, INT_MIN) - 0.r, epsilon);
-
-    EXPECT_EQ(func(INT_MIN, INT_MIN), 1.r);
-  }
-};
-
-#define LIST_DIVI_TESTS(Name, XType, func)                                     \
-  using LlvmLibc##Name##diviTest = DivITest<XType>;                            \
-  TEST_F(LlvmLibc##Name##diviTest, Basic) { testBasic(&func); }                \
-  TEST_F(LlvmLibc##Name##diviTest, Special) { testSpecial(&func); }            \
-  static_assert(true, "Require semicolon.")
diff --git a/libc/test/src/stdfix/FxDiviTest.h b/libc/test/src/stdfix/FxDiviTest.h
new file mode 100644
index 0000000000000..8c3a4038d5de9
--- /dev/null
+++ b/libc/test/src/stdfix/FxDiviTest.h
@@ -0,0 +1,189 @@
+//===-- Utility class to test fxdivi 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 "hdr/signal_macros.h"
+#include "src/__support/CPP/limits.h"
+#include "src/__support/fixed_point/fx_rep.h"
+
+namespace cpp = LIBC_NAMESPACE::cpp;
+
+template <typename FXType, typename IntType>
+class FxDiviTest : public LIBC_NAMESPACE::testing::Test {
+  using FXRep = LIBC_NAMESPACE::fixed_point::FXRep<FXType>;
+
+  static constexpr FXType fx_max = FXRep::MAX();
+  static constexpr FXType fx_min = FXRep::MIN();
+  static constexpr FXType fx_zero = FXRep::ZERO();
+  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);
+  static constexpr int F = FXRep::FRACTION_LEN;
+
+  static constexpr auto abs_diff = [](FXType a, FXType b) {
+    return (a > b) ? (a - b) : (b - a);
+  };
+
+public:
+  typedef FXType (*FxDiviFunc)(IntType, IntType);
+
+  void testBasicNumbers(FxDiviFunc func) {
+    EXPECT_TRUE(abs_diff(func(1, 3), static_cast<FXType>(
+                                         0.33333333333333333333)) <= epsilon);
+    EXPECT_TRUE(abs_diff(func(2, 3), static_cast<FXType>(
+                                         0.66666666666666666667)) <= epsilon);
+    EXPECT_TRUE(abs_diff(func(3, 4), 3 * one_fourth) <= epsilon);
+    EXPECT_TRUE(abs_diff(func(5, 7), static_cast<FXType>(
+                                         0.71428571428571428571)) <= epsilon);
+    if constexpr (is_signed) {
+      EXPECT_TRUE(
+          abs_diff(func(-5, 7), static_cast<FXType>(-0.71428571428571428571)) <=
+          epsilon);
+    }
+    EXPECT_TRUE(abs_diff(func(1043, 2764),
+                         static_cast<FXType>(0.37735166425470332851)) <=
+                epsilon);
+    EXPECT_TRUE(abs_diff(func(60000, 720293),
+                         static_cast<FXType>(0.08329943509099769122)) <=
+                epsilon);
+
+    EXPECT_EQ(func(128, 256), one_half);
+    EXPECT_EQ(func(1, 2), one_half);
+    EXPECT_EQ(func(1, 4), one_fourth);
+    EXPECT_EQ(func(1, 8), one_eighth);
+    EXPECT_EQ(func(1, 16), static_cast<FXType>(0.0625));
+    if constexpr (is_signed) {
+      EXPECT_EQ(func(-1, 2), -one_half);
+      EXPECT_EQ(func(1, -4), -one_fourth);
+      EXPECT_EQ(func(-1, 8), -one_eighth);
+      EXPECT_EQ(func(1, -16), static_cast<FXType>(-0.0625));
+    }
+
+    if constexpr (has_integral) {
+      EXPECT_TRUE(
+          abs_diff(func(27, 23), static_cast<FXType>(1.17391304347826086957)) <=
+          epsilon);
+    }
+  }
+
+  void testEdgeCases(FxDiviFunc func) {
+    constexpr IntType int_max = cpp::numeric_limits<IntType>::max();
+
+    EXPECT_EQ(func(0, 10), fx_zero);
+    if constexpr (is_signed) {
+      EXPECT_EQ(func(0, -10), fx_zero);
+    }
+
+    if constexpr (is_signed && (F < cpp::numeric_limits<IntType>::digits)) {
+      constexpr IntType edge = static_cast<IntType>(1) << F;
+      EXPECT_EQ(func(-edge, edge), static_cast<FXType>(-1));
+      if constexpr (has_integral) {
+        EXPECT_TRUE(abs_diff(func(edge - 1, edge),
+                             static_cast<FXType>(1) - epsilon) <= epsilon);
+      } else {
+        EXPECT_EQ(func(edge - 1, edge), fx_max);
+      }
+    }
+
+    if constexpr (has_integral) {
+      EXPECT_EQ(func(int_max, int_max), static_cast<FXType>(1));
+      EXPECT_TRUE(abs_diff(func(int_max - 1, int_max),
+                           static_cast<FXType>(1) - epsilon) <= epsilon);
+    } else {
+      EXPECT_EQ(func(int_max, int_max), fx_max);
+      EXPECT_EQ(func(int_max - 1, int_max), fx_max);
+      EXPECT_EQ(func(27, 23), fx_max);
+    }
+
+    EXPECT_EQ(func(1, int_max), fx_zero);
+
+    if constexpr (is_signed) {
+      constexpr IntType int_min = cpp::numeric_limits<IntType>::min();
+
+      if constexpr (has_integral) {
+        EXPECT_TRUE(abs_diff(func(int_min, int_max), static_cast<FXType>(-1)) <=
+                    epsilon);
+        EXPECT_EQ(func(int_min, int_min), static_cast<FXType>(1));
+      } else {
+        EXPECT_EQ(func(int_min, int_max), fx_min);
+        EXPECT_EQ(func(int_min, int_min), fx_max);
+      }
+
+      EXPECT_EQ(func(int_min, 1), fx_min);
+      EXPECT_TRUE(abs_diff(func(1, int_min), fx_zero) <= epsilon);
+
+      if constexpr (has_integral) {
+        EXPECT_EQ(func(3, -1), static_cast<FXType>(-3));
+        EXPECT_EQ(func(-3, -1), static_cast<FXType>(3));
+        EXPECT_EQ(func(3, 1), static_cast<FXType>(3));
+        EXPECT_EQ(func(-3, 1), static_cast<FXType>(-3));
+      }
+      EXPECT_EQ(func(int_min, -1), fx_max);
+      EXPECT_EQ(func(int_max, -1), fx_min);
+    }
+  }
+
+  void testWideOperands(FxDiviFunc func) {
+    if constexpr (sizeof(IntType) * 8 > 32) {
+      constexpr IntType big_pow2 = static_cast<IntType>(1) << 40;
+
+      if constexpr (has_integral) {
+        EXPECT_EQ(func(big_pow2, big_pow2), static_cast<FXType>(1));
+      } else {
+        EXPECT_EQ(func(big_pow2, big_pow2), fx_max);
+      }
+      EXPECT_EQ(func(big_pow2, big_pow2 << 1), one_half);
+      if constexpr (is_signed) {
+        EXPECT_EQ(func(-big_pow2, big_pow2), static_cast<FXType>(-1));
+      }
+
+      constexpr IntType big_non_pow2 = big_pow2 + 7;
+      EXPECT_TRUE(abs_diff(func(3, big_non_pow2), fx_zero) <= epsilon);
+      EXPECT_EQ(func(big_non_pow2, big_non_pow2 << 1), one_half);
+    }
+  }
+
+  void testInvalidNumbers(FxDiviFunc func) {
+    EXPECT_DEATH([func] { func(1, 0); }, WITH_SIGNAL(-1));
+    if constexpr (is_signed) {
+      EXPECT_DEATH([func] { func(-1, 0); }, WITH_SIGNAL(-1));
+    }
+  }
+};
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+#define LIST_FXDIVI_TESTS(Name, FXType, IntType, func)                         \
+  using LlvmLibc##Name##Divi##Test = FxDiviTest<FXType, IntType>;              \
+  TEST_F(LlvmLibc##Name##Divi##Test, InvalidNumbers) {                         \
+    testInvalidNumbers(&func);                                                 \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Divi##Test, BasicNumbers) {                           \
+    testBasicNumbers(&func);                                                   \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Divi##Test, EdgeCases) { testEdgeCases(&func); }      \
+  TEST_F(LlvmLibc##Name##Divi##Test, WideOperands) {                           \
+    testWideOperands(&func);                                                   \
+  }                                                                            \
+  static_assert(true, "Require semicolon.")
+#else
+#define LIST_FXDIVI_TESTS(Name, FXType, IntType, func)                         \
+  using LlvmLibc##Name##Divi##Test = FxDiviTest<FXType, IntType>;              \
+  TEST_F(LlvmLibc##Name##Divi##Test, BasicNumbers) {                           \
+    testBasicNumbers(&func);                                                   \
+  }                                                                            \
+  TEST_F(LlvmLibc##Name##Divi##Test, EdgeCases) { testEdgeCases(&func); }      \
+  TEST_F(LlvmLibc##Name##Divi##Test, WideOperands) {                           \
+    testWideOperands(&func);                                                   \
+  }                                                                            \
+  static_assert(true, "Require semicolon.")
+#endif // LIBC_ADD_NULL_CHECKS
diff --git a/libc/test/src/stdfix/kdivi_test.cpp b/libc/test/src/stdfix/kdivi_test.cpp
new file mode 100644
index 0000000000000..faed2835aea56
--- /dev/null
+++ b/libc/test/src/stdfix/kdivi_test.cpp
@@ -0,0 +1,14 @@
+//===-- Unittests for kdivi -----------------------------------------------===//
+//
+// 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 "FxDiviTest.h"
+
+#include "llvm-libc-macros/stdfix-macros.h"
+#include "src/stdfix/kdivi.h"
+
+LIST_FXDIVI_TESTS(k, accum, int, LIBC_NAMESPACE::kdivi);
diff --git a/libc/test/src/stdfix/lkdivi_test.cpp b/libc/test/src/stdfix/lkdivi_test.cpp
new file mode 100644
index 0000000000000..34ee45fe923c5
--- /dev/null
+++ b/libc/test/src/stdfix/lkdivi_test.cpp
@@ -0,0 +1,14 @@
+//===-- Unittests for lkdivi ----------------------------------------------===//
+//
+// 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 "FxDiviTest.h"
+
+#include "llvm-libc-macros/stdfix-macros.h"
+#include "src/stdfix/lkdivi.h"
+
+LIST_FXDIVI_TESTS(lk, long accum, long int, LIBC_NAMESPACE::lkdivi);
diff --git a/libc/test/src/stdfix/lrdivi_test.cpp b/libc/test/src/stdfix/lrdivi_test.cpp
new file mode 100644
index 0000000000000..da72555ad0259
--- /dev/null
+++ b/libc/test/src/stdfix/lrdivi_test.cpp
@@ -0,0 +1,14 @@
+//===-- Unittests for lrdivi ----------------------------------------------===//
+//
+// 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 "FxDiviTest.h"
+
+#include "llvm-libc-macros/stdfix-macros.h"
+#include "src/stdfix/lrdivi.h"
+
+LIST_FXDIVI_TESTS(lr, long fract, long int, LIBC_NAMESPACE::lrdivi);
diff --git a/libc/test/src/stdfix/rdivi_test.cpp b/libc/test/src/stdfix/rdivi_test.cpp
index 10ab366679a36..49fd225d827ae 100644
--- a/libc/test/src/stdfix/rdivi_test.cpp
+++ b/libc/test/src/stdfix/rdivi_test.cpp
@@ -6,9 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "DivITest.h"
+#include "FxDiviTest.h"
 
-#include "llvm-libc-macros/stdfix-macros.h" // fract
+#include "llvm-libc-macros/stdfix-macros.h"
 #include "src/stdfix/rdivi.h"
 
-LIST_DIVI_TESTS(r, fract, LIBC_NAMESPACE::rdivi);
+LIST_FXDIVI_TESTS(r, fract, int, LIBC_NAMESPACE::rdivi);
diff --git a/libc/test/src/stdfix/ukdivi_test.cpp b/libc/test/src/stdfix/ukdivi_test.cpp
new file mode 100644
index 0000000000000..7bd9311eabc05
--- /dev/null
+++ b/libc/test/src/stdfix/ukdivi_test.cpp
@@ -0,0 +1,14 @@
+//===-- Unittests for ukdivi ----------------------------------------------===//
+//
+// 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 "FxDiviTest.h"
+
+#include "llvm-libc-macros/stdfix-macros.h"
+#include "src/stdfix/ukdivi.h"
+
+LIST_FXDIVI_TESTS(uk, unsigned accum, unsigned int, LIBC_NAMESPACE::ukdivi);
diff --git a/libc/test/src/stdfix/ulkdivi_test.cpp b/libc/test/src/stdfix/ulkdivi_test.cpp
new file mode 100644
index 0000000000000..364af81d520c9
--- /dev/null
+++ b/libc/test/src/stdfix/ulkdivi_test.cpp
@@ -0,0 +1,15 @@
+//===-- Unittests for ulkdivi ---------------------------------------------===//
+//
+// 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 "FxDiviTest.h"
+
+#include "llvm-libc-macros/stdfix-macros.h"
+#include "src/stdfix/ulkdivi.h"
+
+LIST_FXDIVI_TESTS(ulk, unsigned long accum, unsigned long int,
+                  LIBC_NAMESPACE::ulkdivi);
diff --git a/libc/test/src/stdfix/ulrdivi_test.cpp b/libc/test/src/stdfix/ulrdivi_test.cpp
new file mode 100644
index 0000000000000..3799c4ace1e2b
--- /dev/null
+++ b/libc/test/src/stdfix/ulrdivi_test.cpp
@@ -0,0 +1,15 @@
+//===-- Unittests for ulrdivi ---------------------------------------------===//
+//
+// 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 "FxDiviTest.h"
+
+#include "llvm-libc-macros/stdfix-macros.h"
+#include "src/stdfix/ulrdivi.h"
+
+LIST_FXDIVI_TESTS(ulr, unsigned long fract, unsigned long int,
+                  LIBC_NAMESPACE::ulrdivi);
diff --git a/libc/test/src/stdfix/urdivi_test.cpp b/libc/test/src/stdfix/urdivi_test.cpp
new file mode 100644
index 0000000000000..971a6a358ea8b
--- /dev/null
+++ b/libc/test/src/stdfix/urdivi_test.cpp
@@ -0,0 +1,14 @@
+//===-- Unittests for urdivi ----------------------------------------------===//
+//
+// 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 "FxDiviTest.h"
+
+#include "llvm-libc-macros/stdfix-macros.h"
+#include "src/stdfix/urdivi.h"
+
+LIST_FXDIVI_TESTS(ur, unsigned fract, unsigned int, LIBC_NAMESPACE::urdivi);

>From bd20ac3393de626cc0881afa37b21484b1c0e6a6 Mon Sep 17 00:00:00 2001
From: sohail103 <sohailraj.satapathy at gmail.com>
Date: Fri, 24 Jul 2026 14:13:05 +0530
Subject: [PATCH 2/2] [libc][stdfix] update fxdivi test cases

---
 libc/test/src/stdfix/FxDiviTest.h | 40 +++++++++++++++++++++++++------
 1 file changed, 33 insertions(+), 7 deletions(-)

diff --git a/libc/test/src/stdfix/FxDiviTest.h b/libc/test/src/stdfix/FxDiviTest.h
index 8c3a4038d5de9..51397977f6f51 100644
--- a/libc/test/src/stdfix/FxDiviTest.h
+++ b/libc/test/src/stdfix/FxDiviTest.h
@@ -42,7 +42,7 @@ class FxDiviTest : public LIBC_NAMESPACE::testing::Test {
                                          0.33333333333333333333)) <= epsilon);
     EXPECT_TRUE(abs_diff(func(2, 3), static_cast<FXType>(
                                          0.66666666666666666667)) <= epsilon);
-    EXPECT_TRUE(abs_diff(func(3, 4), 3 * one_fourth) <= epsilon);
+    EXPECT_EQ(func(3, 4), 3 * one_fourth);
     EXPECT_TRUE(abs_diff(func(5, 7), static_cast<FXType>(
                                          0.71428571428571428571)) <= epsilon);
     if constexpr (is_signed) {
@@ -88,8 +88,7 @@ class FxDiviTest : public LIBC_NAMESPACE::testing::Test {
       constexpr IntType edge = static_cast<IntType>(1) << F;
       EXPECT_EQ(func(-edge, edge), static_cast<FXType>(-1));
       if constexpr (has_integral) {
-        EXPECT_TRUE(abs_diff(func(edge - 1, edge),
-                             static_cast<FXType>(1) - epsilon) <= epsilon);
+        EXPECT_EQ(func(edge - 1, edge), static_cast<FXType>(1) - epsilon);
       } else {
         EXPECT_EQ(func(edge - 1, edge), fx_max);
       }
@@ -105,7 +104,10 @@ class FxDiviTest : public LIBC_NAMESPACE::testing::Test {
       EXPECT_EQ(func(27, 23), fx_max);
     }
 
-    EXPECT_EQ(func(1, int_max), fx_zero);
+    // Cannot EXPECT_EQ even though int_max is a power of 2 because rounding
+    // direction for magnitudes smaller than the representable precision is
+    // implementation defined. The result must be within 1 ulp.
+    EXPECT_TRUE(abs_diff(func(1, int_max), fx_zero) <= epsilon);
 
     if constexpr (is_signed) {
       constexpr IntType int_min = cpp::numeric_limits<IntType>::min();
@@ -120,16 +122,40 @@ class FxDiviTest : public LIBC_NAMESPACE::testing::Test {
       }
 
       EXPECT_EQ(func(int_min, 1), fx_min);
+
+      // Cannot EXPECT_EQ even though int_min is a power of 2 because rounding
+      // direction for magnitudes smaller than the representable precision is
+      // implementation defined. The result must be within 1 ulp.
       EXPECT_TRUE(abs_diff(func(1, int_min), fx_zero) <= epsilon);
 
+      EXPECT_EQ(func(int_min, -1), fx_max);
+      EXPECT_EQ(func(int_max, -1), fx_min);
+    }
+
+    if constexpr (has_integral) {
+      EXPECT_EQ(func(1, 1), static_cast<FXType>(1));
+      EXPECT_EQ(func(2, 1), static_cast<FXType>(2));
+      EXPECT_EQ(func(3, 1), static_cast<FXType>(3));
+    } else {
+      EXPECT_EQ(func(1, 1), fx_max);
+      EXPECT_EQ(func(2, 1), fx_max);
+      EXPECT_EQ(func(3, 1), fx_max);
+    }
+
+    if constexpr (is_signed) {
+      EXPECT_EQ(func(-1, 1), static_cast<FXType>(-1));
+      EXPECT_EQ(func(1, -1), static_cast<FXType>(-1));
+
       if constexpr (has_integral) {
+        EXPECT_EQ(func(-1, -1), static_cast<FXType>(1));
         EXPECT_EQ(func(3, -1), static_cast<FXType>(-3));
         EXPECT_EQ(func(-3, -1), static_cast<FXType>(3));
-        EXPECT_EQ(func(3, 1), static_cast<FXType>(3));
         EXPECT_EQ(func(-3, 1), static_cast<FXType>(-3));
+      } else {
+        EXPECT_EQ(func(-1, -1), fx_max);
+        EXPECT_EQ(func(3, -1), fx_min);
+        EXPECT_EQ(func(-3, -1), fx_max);
       }
-      EXPECT_EQ(func(int_min, -1), fx_max);
-      EXPECT_EQ(func(int_max, -1), fx_min);
     }
   }
 



More information about the libc-commits mailing list