Issue #13440 has been updated by stomar (Marcus Stollsteimer).


@nobu

Do you mind when I simplify the test and also reduce the number of tested values (50.000 seems more than necessary, even for 1000 cases, i.e. step 0.05, there would be more than 70 failures before the fix).

I'd change it like this:

``` diff
From 117e6af6858d5215ba17585ebf79bc1c33f2bd5e Mon Sep 17 00:00:00 2001
From: Marcus Stollsteimer <sto.mar / web.de>
Date: Sat, 15 Apr 2017 19:35:22 +0200
Subject: * test/ruby/test_integer.rb: simplify test for Integer.sqrt

---
 test/ruby/test_integer.rb |   13 +++++--------
 1 file changed, 5 insertions(+), 8 deletions(-)

diff --git a/test/ruby/test_integer.rb b/test/ruby/test_integer.rb
index 963c6b7..db47827 100644
--- a/test/ruby/test_integer.rb
+++ b/test/ruby/test_integer.rb
@@ -490,15 +490,12 @@ def test_square_root
     end
 
     bug13440 = '[ruby-core:80696] [Bug #13440]'
-    too_big = []
-    too_small = []
-    0.step(to: 50, by: 0.001) do |i|
+    failures = []
+    0.step(to: 50, by: 0.05) do |i|
       n = (10**i).to_i
-      int_root = Integer.sqrt(n)
-      too_big   << n  if int_root*int_root > n
-      too_small << n  if (int_root+1)*(int_root+1) <= n
+      root = Integer.sqrt(n)
+      failures << n  unless root*root <= n && (root+1)*(root+1) > n
     end
-    assert_empty(too_big, bug13440)
-    assert_empty(too_small, bug13440)
+    assert_empty(failures, bug13440)
   end
 end
-- 
1.7.9.5

```


----------------------------------------
Bug #13440: Integer.sqrt produces wrong results
https://bugs.ruby-lang.org/issues/13440#change-64248

* Author: stomar (Marcus Stollsteimer)
* Status: Closed
* Priority: Normal
* Assignee: 
* Target version: 2.5
* ruby -v: ruby 2.5.0dev (2017-04-14 trunk 58353) [i686-linux]
* Backport: 2.2: DONTNEED, 2.3: DONTNEED, 2.4: DONTNEED
----------------------------------------
The new `Integer.sqrt` method produces wrong results, e.g. for

    38815036599065022652481536
    38904514499428047971680256

and (many) other numbers.

Note that these numbers were picked selectively (these are not the 2 smallest examples), and also that they are well in the range where Math.sqrt(n).to_i still gives correct results (Float precision still sufficient). However, the latter point is only incidental, I also found much bigger examples, in the range where Math.sqrt is useless.

``` ruby
numbers = [
  38815036599065022652481534,
  38815036599065022652481535,
  38815036599065022652481536,  # fails
  38815036599065022652481537,  # fails
  38904514499428047971680254,
  38904514499428047971680255,
  38904514499428047971680256,  # fails
  38904514499428047971680257,  # fails
  40271703432545948091285502,
  40271703432545948091285503,
  40271703432545948091285504,  # fails
  40271703432545948091285505,  # fails
  1442115351524865087017488818362939538217338142719,
  1442115351524865087017488818362939538217338142720,  # fails
]

def validate(number, root)
  root**2 <= number && (root+1)**2 > number
end

numbers.map {|n| [Integer.sqrt(n), validate(n, Integer.sqrt(n))] }
  # => [[6230171474290, true],
  #     [6230171474290, true],
  #     [6230171582464, false],
  #     [6230171582464, false],
  #     [6237348354824, true],
  #     [6237348354824, true],
  #     [6237348429824, false],
  #     [6237348429824, false],
  #     [6345999009812, true],
  #     [6345999009812, true],
  #     [6345999122432, false],
  #     [6345999122432, false],
  #     [1200881073014669961418100, true],
  #     [1200881075629054276665344, false]]
numbers.map {|n| [Math.sqrt(n).to_i, validate(n, Math.sqrt(n).to_i)] }
  # => [[6230171474290, true],
  #     [6230171474290, true],
  #     [6230171474290, true],
  #     [6230171474290, true],
  #     [6237348354824, true],
  #     [6237348354824, true],
  #     [6237348354824, true],
  #     [6237348354824, true],
  #     [6345999009812, true],
  #     [6345999009812, true],
  #     [6345999009812, true],
  #     [6345999009812, true],
  #     [1200881073014669968408576, false],
  #     [1200881073014669968408576, false]]

1.size  # => 4 (32-bit system)
```

Interestingly, I found only examples (yet) where `Integer.sqrt` produces results that are (much) too big.

It was rather too easy to find those; here's what I did:

``` ruby
too_big, too_small = [], []
total = 0

0.step(to: 50, by: 0.001) do |i|
  n = (10**i).to_i
  raise  unless n.class == Integer  # just to make sure...

  int_root = Integer.sqrt(n)

  total += 1
  too_big   << n  if int_root*int_root > n
  too_small << n  if (int_root+1)*(int_root+1) <= n
end

puts "total: #{total}"
puts
puts "too small (#{too_small.size}):", too_small
puts
puts "too big (#{too_big.size}):"

puts too_big[0..9]
puts "..."
puts too_big[-10..-1]

# >> total: 50001
# >> 
# >> too small (0):
# >> 
# >> too big (3579):
# >> 38815036599065022652481536
# >> 38904514499428047971680256
# >> 38994198667654436652843008
# >> 39810717055349854497144832
# >> 40271703432545948091285504
# >> 40364539296760648765014016
# >> 40457589169744204087164928
# >> 40644332916521443952427008
# >> 40926065973001261821198336
# >> 42169650342858222399913984
# >> ...
# >> 1324341535194664238462783233069825155347351863296
# >> 1367728825595857894544027656111101204949201059840
# >> 1370881766164855075247880701478883966489888555008
# >> 1409288798421877644341184857286932334307738386432
# >> 1412537544622749693814622477014802231398687047680
# >> 1415793779957092451680042925874609046246970621952
# >> 1422328787122815537257372883177955123216529752064
# >> 1432187899273539462185319204962288499459270639616
# >> 1438798578255849634982033297755877609401625870336
# >> 1442115351524865087017488818362939538217338142720
```




-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>