summaryrefslogtreecommitdiff
path: root/numeric.c
diff options
context:
space:
mode:
authorYusuke Endoh <[email protected]>2025-04-06 18:36:06 +0900
committerYusuke Endoh <[email protected]>2025-04-07 11:08:10 +0900
commit3a7b9ca93b91dcc086b9ac8b9957e59268f9493b (patch)
treeb129fb6d901cb8baa05031e67db38ae201883dd3 /numeric.c
parente25889951f39aff6e3c16ecee10e678912454e69 (diff)
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]
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/13076
Diffstat (limited to 'numeric.c')
-rw-r--r--numeric.c6
1 files changed, 5 insertions, 1 deletions
diff --git a/numeric.c b/numeric.c
index dcf4cf5c04..d3affed804 100644
--- a/numeric.c
+++ b/numeric.c
@@ -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