[前][次][番号順一覧][スレッド一覧]

ruby-changes:66364

From: Jeremy <ko1@a...>
Date: Sun, 30 May 2021 00:56:36 +0900 (JST)
Subject: [ruby-changes:66364] f516379853 (master): Fix Enumerator::ArithmeticSequence handling of float ranges

https://git.ruby-lang.org/ruby.git/commit/?id=f516379853

From f516379853f36d143d820c55d5eeaa9fc410ef52 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Thu, 29 Apr 2021 12:51:05 -0700
Subject: Fix Enumerator::ArithmeticSequence handling of float ranges

Depending on the float range, there could be an off-by-one error,
where the last result that should be in the range was missed. Fix
this by checking if the computed value for the expected value
outside the range is still inside the range, and if so, increment
the step size.

Fixes [Bug #16612]
---
 numeric.c                         | 20 ++++++++++++++++++--
 spec/ruby/core/range/step_spec.rb | 38 +++++++++++++++++++++-----------------
 test/ruby/test_float.rb           | 11 +++++++++++
 3 files changed, 50 insertions(+), 19 deletions(-)

diff --git a/numeric.c b/numeric.c
index 17a401f..d0538ca 100644
--- a/numeric.c
+++ b/numeric.c
@@ -2409,11 +2409,11 @@ ruby_float_step_size(double beg, double end, double unit, int excl) https://github.com/ruby/ruby/blob/trunk/numeric.c#L2409
     if (unit == 0) {
         return HUGE_VAL;
     }
-    n= (end - beg)/unit;
-    err = (fabs(beg) + fabs(end) + fabs(end-beg)) / fabs(unit) * epsilon;
     if (isinf(unit)) {
 	return unit > 0 ? beg <= end : beg >= end;
     }
+    n= (end - beg)/unit;
+    err = (fabs(beg) + fabs(end) + fabs(end-beg)) / fabs(unit) * epsilon;
     if (err>0.5) err=0.5;
     if (excl) {
 	if (n<=0) return 0;
@@ -2421,10 +2421,26 @@ ruby_float_step_size(double beg, double end, double unit, int excl) https://github.com/ruby/ruby/blob/trunk/numeric.c#L2421
 	    n = 0;
 	else
 	    n = floor(n - err);
+        if (beg < end) {
+            if ((n+1)*unit+beg < end)
+                n++;
+        }
+        else if (beg > end) {
+            if ((n+1)*unit+beg > end)
+                n++;
+        }
     }
     else {
 	if (n<0) return 0;
 	n = floor(n + err);
+        if (beg < end) {
+            if ((n+1)*unit+beg <= end)
+                n++;
+        }
+        else if (beg > end) {
+            if ((n+1)*unit+beg >= end)
+               n++;
+        }
     }
     return n+1;
 }
diff --git a/spec/ruby/core/range/step_spec.rb b/spec/ruby/core/range/step_spec.rb
index e284551..1c2e307 100644
--- a/spec/ruby/core/range/step_spec.rb
+++ b/spec/ruby/core/range/step_spec.rb
@@ -207,10 +207,12 @@ describe "Range#step" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/range/step_spec.rb#L207
         ScratchPad.recorded.should eql([-1.0, -0.5, 0.0, 0.5])
       end
 
-      it "returns Float values of 'step * n + begin < end'" do
-        (1.0...6.4).step(1.8) { |x| ScratchPad << x }
-        (1.0...55.6).step(18.2) { |x| ScratchPad << x }
-        ScratchPad.recorded.should eql([1.0, 2.8, 4.6, 1.0, 19.2, 37.4])
+      ruby_version_is '3.1' do
+        it "returns Float values of 'step * n + begin < end'" do
+          (1.0...6.4).step(1.8) { |x| ScratchPad << x }
+          (1.0...55.6).step(18.2) { |x| ScratchPad << x }
+          ScratchPad.recorded.should eql([1.0, 2.8, 4.6, 1.0, 19.2, 37.4, 55.599999999999994])
+        end
       end
 
       it "handles infinite values at either end" do
@@ -457,19 +459,21 @@ describe "Range#step" do https://github.com/ruby/ruby/blob/trunk/spec/ruby/core/range/step_spec.rb#L459
           (-1.0...1.0).step(0.5).size.should == 4
         end
 
-        it "returns the range size when there's no step_size" do
-          (-2..2).step.size.should == 5
-          (-2.0..2.0).step.size.should == 5
-          (-2..2.0).step.size.should == 5
-          (-2.0..2).step.size.should == 5
-          (1.0..6.4).step(1.8).size.should == 4
-          (1.0..12.7).step(1.3).size.should == 10
-          (-2...2).step.size.should == 4
-          (-2.0...2.0).step.size.should == 4
-          (-2...2.0).step.size.should == 4
-          (-2.0...2).step.size.should == 4
-          (1.0...6.4).step(1.8).size.should == 3
-          (1.0...55.6).step(18.2).size.should == 3
+        ruby_version_is '3.1' do
+          it "returns the range size when there's no step_size" do
+            (-2..2).step.size.should == 5
+            (-2.0..2.0).step.size.should == 5
+            (-2..2.0).step.size.should == 5
+            (-2.0..2).step.size.should == 5
+            (1.0..6.4).step(1.8).size.should == 4
+            (1.0..12.7).step(1.3).size.should == 10
+            (-2...2).step.size.should == 4
+            (-2.0...2.0).step.size.should == 4
+            (-2...2.0).step.size.should == 4
+            (-2.0...2).step.size.should == 4
+            (1.0...6.4).step(1.8).size.should == 3
+            (1.0...55.6).step(18.2).size.should == 4
+          end
         end
 
         it "returns nil with begin and end are String" do
diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb
index fbf0d87..ca61645 100644
--- a/test/ruby/test_float.rb
+++ b/test/ruby/test_float.rb
@@ -882,6 +882,11 @@ class TestFloat < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_float.rb#L882
     end
 
     assert_equal([5.0, 4.0, 3.0, 2.0], 5.0.step(1.5, -1).to_a)
+
+    assert_equal(11, ((0.24901079128550474)..(340.2500808898068)).step(34.00010700985213).to_a.size)
+    assert_equal(11, ((0.24901079128550474)..(340.25008088980684)).step(34.00010700985213).to_a.size)
+    assert_equal(11, ((-0.24901079128550474)..(-340.2500808898068)).step(-34.00010700985213).to_a.size)
+    assert_equal(11, ((-0.24901079128550474)..(-340.25008088980684)).step(-34.00010700985213).to_a.size)
   end
 
   def test_step2
@@ -893,6 +898,7 @@ class TestFloat < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_float.rb#L898
       a = rand
       b = a+rand*1000
       s = (b - a) / 10
+      b = a + s*10
       seq = (a...b).step(s)
       assert_equal(10, seq.to_a.length, seq.inspect)
     end
@@ -903,6 +909,11 @@ class TestFloat < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_float.rb#L909
     (1.0 ... e).step(1E-16) do |n|
       assert_operator(n, :<=, e)
     end
+
+    assert_equal(10, ((0.24901079128550474)...(340.2500808898068)).step(34.00010700985213).to_a.size)
+    assert_equal(11, ((0.24901079128550474)...(340.25008088980684)).step(34.00010700985213).to_a.size)
+    assert_equal(10, ((-0.24901079128550474)...(-340.2500808898068)).step(-34.00010700985213).to_a.size)
+    assert_equal(11, ((-0.24901079128550474)...(-340.25008088980684)).step(-34.00010700985213).to_a.size)
   end
 
   def test_singleton_method
-- 
cgit v1.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]