[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