[libc-commits] [libc] [llvm] [libc][math][c23] Improve rsqrtf16() function (PR #160639)

via libc-commits libc-commits at lists.llvm.org
Sat Jun 6 08:31:51 PDT 2026


================
@@ -24,6 +24,193 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace math {
 
+namespace rsqrtf16_internal {
+
+// Fixed-point computations below use Q29: the integer N represents
+// N * 2^-29.  Multiplying two Q29 values produces a Q58 value, so products are
+// shifted right by RSQRT_FRACTION_BITS to return to Q29.
+LIBC_INLINE_VAR constexpr int RSQRT_FRACTION_BITS = 29;
+LIBC_INLINE_VAR constexpr int64_t ONE = int64_t(1) << RSQRT_FRACTION_BITS;
+LIBC_INLINE_VAR constexpr int64_t THREE_HALVES = 3 * (ONE >> 1);
+
+// Midpoint lookup table for 1/sqrt(x) on 16 sub-intervals of [0.5;1).
+// Values are stored in Q29 fixed-point format.  The Newton step and exact
+// rounding below correct the seed before producing the final half result.
+LIBC_INLINE_VAR constexpr uint32_t RSQRT_APPROX[16] = {
+    747'657'839, 725'981'977, 706'088'274, 687'745'184,
+    670'761'200, 654'976'372, 640'255'922, 626'485'368,
+    613'566'757, 601'415'717, 589'959'130, 579'133'272,
+    568'882'316, 559'157'115, 549'914'212, 541'115'017,
+};
+LIBC_INLINE_VAR constexpr int64_t ONE_OVER_SQRT2 = 0x16a09e60;
+
+LIBC_INLINE constexpr int floor_log2(uint64_t x) {
+  return 63 - cpp::countl_zero(x);
+}
+
+LIBC_INLINE constexpr int64_t initial_approximation(uint32_t x_mant) {
+  return RSQRT_APPROX[(x_mant - 0x0400) >> 6];
+}
+
+LIBC_INLINE constexpr int64_t newton_raphson(uint32_t m, int64_t y) {
+  // Refine y ~= 1/sqrt(m) with:
+  //   y_{n+1} = y_n * (1.5 - 0.5 * m * y_n^2)
+  // where both m and y are stored in Q29.
+  int64_t y2 = (y * y) >> RSQRT_FRACTION_BITS;
+  int64_t my2 = (static_cast<int64_t>(m) * y2) >> RSQRT_FRACTION_BITS;
+  int64_t factor = THREE_HALVES - (my2 >> 1);
+  return (y * factor) >> RSQRT_FRACTION_BITS;
+}
+
+LIBC_INLINE constexpr uint16_t fixed_to_half_bits(uint64_t y, int scale_exp) {
+  int y_log2 = floor_log2(y);
+  int out_exp = scale_exp + y_log2 - RSQRT_FRACTION_BITS;
+  int biased_exp = out_exp + 15;
+
+  uint32_t out_sig = y_log2 >= 10 ? static_cast<uint32_t>(y >> (y_log2 - 10))
+                                  : static_cast<uint32_t>(y << (10 - y_log2));
+
+  if (biased_exp <= 0)
+    return 0x0400;
+  if (biased_exp >= 31)
+    return 0x7bff;
+
+  return static_cast<uint16_t>((biased_exp << 10) | (out_sig & 0x3ff));
+}
+
+struct ApproxResult {
+  uint16_t value;
+  uint32_t x_sig;
+  int x_exp;
+};
+
+LIBC_INLINE constexpr ApproxResult approximate_rsqrt(uint16_t x_abs) {
+  uint32_t x_mant = x_abs & 0x03ff;
+  uint32_t x_sig = x_mant;
+  int x_exp = -24;
+  int exponent = 0;
+
+  // Decompose the finite positive input as:
+  //   x = m * 2^exponent, with 0.5 <= m < 1.
+  // `x_sig` and `x_exp` keep the exact input as x_sig * 2^x_exp for the integer
+  // rounding test below.
+  if (x_abs >= 0x0400) {
+    int biased_exp = static_cast<int>(x_abs >> 10);
----------------
lntue wrote:

Can we demystify these constants, either via comments or via `FPBits` constants?

https://github.com/llvm/llvm-project/pull/160639


More information about the libc-commits mailing list