Fix Integer.sqrt to never exceed actual value

`Integer.sqrt` uses `sqrt(3)` from libm for small values.
This method must return a value less than or equal to the actual integer
square root, but libm's sqrt does not always guarantee that.

This change corrects that by decrementing the result if necessary.

Fixes [Bug #21217]
This commit is contained in:
Yusuke Endoh 2025-04-06 18:36:06 +09:00
parent e25889951f
commit 3a7b9ca93b
Notes: git 2025-04-07 02:08:28 +00:00
2 changed files with 9 additions and 1 deletions

View File

@ -5978,7 +5978,11 @@ prefix##_isqrt(argtype n) \
while ((t = n/x) < (argtype)x) x = (rettype)((x + t) >> 1); \
return x; \
} \
return (rettype)sqrt(argtype##_TO_DOUBLE(n)); \
rettype x = (rettype)sqrt(argtype##_TO_DOUBLE(n)); \
/* libm sqrt may returns a larger approximation than actual. */ \
/* Our isqrt always returns a smaller approximation. */ \
if (x * x > n) x--; \
return x; \
}
#if SIZEOF_LONG*CHAR_BIT > DBL_MANT_DIG

View File

@ -708,6 +708,10 @@ class TestInteger < Test::Unit::TestCase
assert_equal(x, Integer.sqrt(x ** 2), "[ruby-core:95453]")
end
def test_bug_21217
assert_equal(0x10000 * 2**10, Integer.sqrt(0x100000008 * 2**20))
end
def test_fdiv
assert_equal(1.0, 1.fdiv(1))
assert_equal(0.5, 1.fdiv(2))