[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