[llvm] [APInt] Provide isqrt (floor of square root) instead of sqrt (rounded) (PR #197406)

Jay Foad via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 10 08:04:15 PDT 2026


https://github.com/jayfoad updated https://github.com/llvm/llvm-project/pull/197406

>From b1cf12968278f3a2be6fe6d8615fe1409b5ad956 Mon Sep 17 00:00:00 2001
From: Jay Foad <jay.foad at amd.com>
Date: Wed, 13 May 2026 10:34:57 +0100
Subject: [PATCH 1/2] [APInt] Provide isqrt (floor of square root) instead of
 sqrt (rounded)

This simplifies both the implementation and the only in-tree user.

I changed the name to avoid silently changing the behavour of an
existing function that might have out-of-tree users. The new name is
inspired by https://en.wikipedia.org/wiki/Integer_square_root.
---
 llvm/docs/ReleaseNotes.md        |  3 +++
 llvm/include/llvm/ADT/APInt.h    |  4 +--
 llvm/lib/Support/APInt.cpp       | 45 +++++++++-----------------------
 llvm/unittests/ADT/APIntTest.cpp | 26 +++++++++++++-----
 4 files changed, 37 insertions(+), 41 deletions(-)

diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 582793db6d4be..5652b5901ad33 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -125,6 +125,9 @@ Makes programs 10x faster by doing Special New Thing.
   in bitcode, e.g. `malloc`. Not yet supported on MachO or when using
   distributed ThinLTO. 
 
+* ``APInt::sqrt`` (square root rounded to nearest integer) has been replaced
+  with ``APInt::isqrt`` (floor of square root).
+
 ### Changes to building LLVM
 
 ### Changes to TableGen
diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index 859cbd5c07147..d28eff6bef886 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -1809,8 +1809,8 @@ class [[nodiscard]] APInt {
     return logBase2();
   }
 
-  /// Compute the square root.
-  LLVM_ABI APInt sqrt() const;
+  /// Compute the floor of the square root of the unsigned value.
+  LLVM_ABI APInt isqrt() const;
 
   /// Get the absolute value.  If *this is < 0 then return -(*this), otherwise
   /// *this.  Note that the "most negative" signed number (e.g. -128 for 8 bit
diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp
index df3616abd7dcf..40bfbd7c7d88e 100644
--- a/llvm/lib/Support/APInt.cpp
+++ b/llvm/lib/Support/APInt.cpp
@@ -1239,7 +1239,7 @@ unsigned APInt::nearestLogBase2() const {
 // the libc sqrt function is called. The result is rounded and then converted
 // back to a uint64_t which is then used to construct the result. Finally,
 // the Babylonian method for computing square roots is used.
-APInt APInt::sqrt() const {
+APInt APInt::isqrt() const {
 
   // Determine the magnitude of the value.
   unsigned magnitude = getActiveBits();
@@ -1248,13 +1248,12 @@ APInt APInt::sqrt() const {
   // rounding errors in libc sqrt for small values.
   if (magnitude <= 5) {
     static const uint8_t results[32] = {
-      /*     0 */ 0,
-      /*  1- 2 */ 1, 1,
-      /*  3- 6 */ 2, 2, 2, 2,
-      /*  7-12 */ 3, 3, 3, 3, 3, 3,
-      /* 13-20 */ 4, 4, 4, 4, 4, 4, 4, 4,
-      /* 21-30 */ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
-      /*    31 */ 6
+        /*     0 */ 0,
+        /*  1- 3 */ 1, 1, 1,
+        /*  4- 8 */ 2, 2, 2, 2, 2,
+        /*  9-15 */ 3, 3, 3, 3, 3, 3, 3,
+        /* 16-24 */ 4, 4, 4, 4, 4, 4, 4, 4, 4,
+        /* 25-31 */ 5, 5, 5, 5, 5, 5, 5,
     };
     return APInt(BitWidth, results[ (isSingleWord() ? U.VAL : U.pVal[0]) ]);
   }
@@ -1264,9 +1263,9 @@ APInt APInt::sqrt() const {
   // libc sqrt function which will probably use a hardware sqrt computation.
   // This should be faster than the algorithm below.
   if (magnitude < 52) {
-    return APInt(BitWidth,
-                 uint64_t(::round(::sqrt(double(isSingleWord() ? U.VAL
-                                                               : U.pVal[0])))));
+    return APInt(
+        BitWidth,
+        uint64_t(::floor(::sqrt(double(isSingleWord() ? U.VAL : U.pVal[0])))));
   }
 
   // Okay, all the short cuts are exhausted. We must compute it. The following
@@ -1294,23 +1293,7 @@ APInt APInt::sqrt() const {
       break;
     x_old = x_new;
   }
-
-  // Make sure we return the closest approximation
-  // NOTE: The rounding calculation below is correct. It will produce an
-  // off-by-one discrepancy with results from pari/gp. That discrepancy has been
-  // determined to be a rounding issue with pari/gp as it begins to use a
-  // floating point representation after 192 bits. There are no discrepancies
-  // between this algorithm and pari/gp for bit widths < 192 bits.
-  APInt square(x_old * x_old);
-  if (this->ult(square))
-    return x_old;
-  APInt delta(2 * x_old + 1);
-  APInt offset(*this - square);
-  assert(offset.ule(delta) && "Error in APInt::sqrt computation");
-  APInt midpoint(delta.udiv(two));
-  if (offset.ult(midpoint))
-    return x_old;
-  return x_old + 1;
+  return x_old;
 }
 
 /// \returns the multiplicative inverse of an odd APInt modulo 2^BitWidth.
@@ -2991,14 +2974,10 @@ llvm::APIntOps::SolveQuadraticEquationWrap(APInt A, APInt B, APInt C,
 
   APInt D = SqrB - 4*A*C;
   assert(D.isNonNegative() && "Negative discriminant");
-  APInt SQ = D.sqrt();
+  APInt SQ = D.isqrt();
 
   APInt Q = SQ * SQ;
   bool InexactSQ = Q != D;
-  // The calculated SQ may actually be greater than the exact (non-integer)
-  // value. If that's the case, decrement SQ to get a value that is lower.
-  if (Q.sgt(D))
-    SQ -= 1;
 
   APInt X;
   APInt Rem;
diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp
index 3c0446867b14b..5badc7799f208 100644
--- a/llvm/unittests/ADT/APIntTest.cpp
+++ b/llvm/unittests/ADT/APIntTest.cpp
@@ -9,6 +9,7 @@
 #include "llvm/ADT/APInt.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Sequence.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Support/Alignment.h"
@@ -4010,11 +4011,24 @@ TEST(APIntTest, clmulh) {
             21845);
 }
 
-TEST(APIntTest, sqrt) {
-  EXPECT_EQ(APInt::getMaxValue(64).sqrt(), 4294967296U);
-  EXPECT_EQ(APInt::getMaxValue(128).sqrt(),
-            APInt(128, "18446744073709551616", 10));
-  EXPECT_EQ(APInt::getMaxValue(256).sqrt(),
-            APInt(256, "340282366920938463463374607431768211456", 10));
+TEST(APIntTest, isqrt) {
+  EXPECT_EQ(APInt::getMaxValue(64).isqrt(), 4294967295U);
+  EXPECT_EQ(APInt::getMaxValue(128).isqrt(),
+            APInt(128, "18446744073709551615", 10));
+  EXPECT_EQ(APInt::getMaxValue(256).isqrt(),
+            APInt(256, "340282366920938463463374607431768211455", 10));
+  // Exhaustive test for smallish inputs.
+  for (unsigned N : seq(1000u)) {
+    unsigned S = APInt(32, N).isqrt().getZExtValue();
+    EXPECT_LE(S * S, N);
+    EXPECT_GT((S + 1) * (S + 1), N);
+  }
+  // Test some values around an arbitrary square larger than 2^52.
+  for (uint64_t I : seq(1000ull)) {
+    uint64_t N = 87654321ull * 87654321ull + I - 500ull;
+    uint64_t S = APInt(64, N).isqrt().getZExtValue();
+    EXPECT_LE(S * S, N);
+    EXPECT_GT((S + 1) * (S + 1), N);
+  }
 }
 } // end anonymous namespace

>From 99561d08da546c5ea10d154948a72f0206a8e1e0 Mon Sep 17 00:00:00 2001
From: Jay Foad <jay.foad at amd.com>
Date: Wed, 10 Jun 2026 15:38:46 +0100
Subject: [PATCH 2/2] Change name from isqrt to sqrtFloor

---
 llvm/docs/ReleaseNotes.md        |  2 +-
 llvm/include/llvm/ADT/APInt.h    |  2 +-
 llvm/lib/Support/APInt.cpp       |  4 ++--
 llvm/unittests/ADT/APIntTest.cpp | 12 ++++++------
 4 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index f448fdda38f42..bdfbfc3f70533 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -152,7 +152,7 @@ Makes programs 10x faster by doing Special New Thing.
   TableGen to improve compile-time.
 
 * ``APInt::sqrt`` (square root rounded to nearest integer) has been replaced
-  with ``APInt::isqrt`` (floor of square root).
+  with ``APInt::sqrtFloor`` (floor of square root).
 
 ### Changes to building LLVM
 
diff --git a/llvm/include/llvm/ADT/APInt.h b/llvm/include/llvm/ADT/APInt.h
index 6976349cf6fee..99eb758cc7a6f 100644
--- a/llvm/include/llvm/ADT/APInt.h
+++ b/llvm/include/llvm/ADT/APInt.h
@@ -1810,7 +1810,7 @@ class [[nodiscard]] APInt {
   }
 
   /// Compute the floor of the square root of the unsigned value.
-  LLVM_ABI APInt isqrt() const;
+  LLVM_ABI APInt sqrtFloor() const;
 
   /// Get the absolute value.  If *this is < 0 then return -(*this), otherwise
   /// *this.  Note that the "most negative" signed number (e.g. -128 for 8 bit
diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp
index 414a89ca72ea0..221d642ae8539 100644
--- a/llvm/lib/Support/APInt.cpp
+++ b/llvm/lib/Support/APInt.cpp
@@ -1239,7 +1239,7 @@ unsigned APInt::nearestLogBase2() const {
 // the libc sqrt function is called. The result is rounded and then converted
 // back to a uint64_t which is then used to construct the result. Finally,
 // the Babylonian method for computing square roots is used.
-APInt APInt::isqrt() const {
+APInt APInt::sqrtFloor() const {
 
   // Determine the magnitude of the value.
   unsigned magnitude = getActiveBits();
@@ -2974,7 +2974,7 @@ llvm::APIntOps::SolveQuadraticEquationWrap(APInt A, APInt B, APInt C,
 
   APInt D = SqrB - 4*A*C;
   assert(D.isNonNegative() && "Negative discriminant");
-  APInt SQ = D.isqrt();
+  APInt SQ = D.sqrtFloor();
 
   APInt Q = SQ * SQ;
   bool InexactSQ = Q != D;
diff --git a/llvm/unittests/ADT/APIntTest.cpp b/llvm/unittests/ADT/APIntTest.cpp
index eb1cae43532bb..39005969c5bad 100644
--- a/llvm/unittests/ADT/APIntTest.cpp
+++ b/llvm/unittests/ADT/APIntTest.cpp
@@ -4011,22 +4011,22 @@ TEST(APIntTest, clmulh) {
             21845);
 }
 
-TEST(APIntTest, isqrt) {
-  EXPECT_EQ(APInt::getMaxValue(64).isqrt(), 4294967295U);
-  EXPECT_EQ(APInt::getMaxValue(128).isqrt(),
+TEST(APIntTest, sqrtFloor) {
+  EXPECT_EQ(APInt::getMaxValue(64).sqrtFloor(), 4294967295U);
+  EXPECT_EQ(APInt::getMaxValue(128).sqrtFloor(),
             APInt(128, "18446744073709551615", 10));
-  EXPECT_EQ(APInt::getMaxValue(256).isqrt(),
+  EXPECT_EQ(APInt::getMaxValue(256).sqrtFloor(),
             APInt(256, "340282366920938463463374607431768211455", 10));
   // Exhaustive test for smallish inputs.
   for (unsigned N : seq(1000u)) {
-    unsigned S = APInt(32, N).isqrt().getZExtValue();
+    unsigned S = APInt(32, N).sqrtFloor().getZExtValue();
     EXPECT_LE(S * S, N);
     EXPECT_GT((S + 1) * (S + 1), N);
   }
   // Test some values around an arbitrary square larger than 2^52.
   for (uint64_t I : seq(1000ull)) {
     uint64_t N = 87654321ull * 87654321ull + I - 500ull;
-    uint64_t S = APInt(64, N).isqrt().getZExtValue();
+    uint64_t S = APInt(64, N).sqrtFloor().getZExtValue();
     EXPECT_LE(S * S, N);
     EXPECT_GT((S + 1) * (S + 1), N);
   }



More information about the llvm-commits mailing list