From 02ad18dc580bffe5048cbd7d99b6a7ecb3784e07 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 23 May 2026 11:37:24 +0900 Subject: [PATCH 1/6] Simplify save-stats.rb without Pathname --- .github/workflows/tarball-ubuntu.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tarball-ubuntu.yml b/.github/workflows/tarball-ubuntu.yml index 13606c7eee03b3..f0e773b5269a60 100644 --- a/.github/workflows/tarball-ubuntu.yml +++ b/.github/workflows/tarball-ubuntu.yml @@ -69,16 +69,16 @@ jobs: [ Dir.home, ].each do |dir| - Pathname(dir).each_child do |pn| - st = pn.stat + Dir.each_child(dir) do |pn| + st = File.stat(pn) if st.file? - content = Digest::SHA1.hexdigest(pn.read) + content = Digest::SHA1.file(pn).hexdigest elsif st.directory? && st.nlink <= 10 - content = pn.children.map(&:basename).map(&:to_s).sort + content = Dir.children(pn).sort end - out << [pn.to_s, "%o"%st.mode, st.nlink, st.uid, st.gid, st.size, content].to_s + out << [pn, "%o"%st.mode, st.nlink, st.uid, st.gid, st.size, content].to_s rescue - out << [pn.to_s, $!.inspect].to_s + out << [pn, $!.inspect].to_s end end File.open(ARGV.shift, "w") do |io| From 4f683a2820a19840ca7aaedc1a8bbd03130b0a47 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 23 May 2026 11:38:25 +0900 Subject: [PATCH 2/6] Skip tarball tests on documents-only commits Except for non-development that runs build only. --- .github/workflows/tarball-test.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/tarball-test.yml b/.github/workflows/tarball-test.yml index db49977cbff285..5adc2708ffb51d 100644 --- a/.github/workflows/tarball-test.yml +++ b/.github/workflows/tarball-test.yml @@ -35,7 +35,18 @@ jobs: BRANCH: master outputs: branch: ${{ env.BRANCH }} + skip: ${{ steps.skipping.outputs.skip }} steps: + - id: skipping + run: + echo 'skip=true' >> $GITHUB_OUTPUT + if: >- + ${{(false + || contains(github.event.head_commit.message, '[DOC]') + || contains(github.event.pull_request.title, '[DOC]') + || contains(github.event.pull_request.labels.*.name, 'Documentation') + || (github.event.pull_request.user.login == 'dependabot[bot]') + )}} - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 @@ -55,6 +66,7 @@ jobs: ubuntu: needs: tarball + if: ${{ ! needs.tarball.outputs.skip }} uses: ./.github/workflows/tarball-ubuntu.yml with: archname: snapshot-${{ needs.tarball.outputs.branch }} @@ -63,6 +75,7 @@ jobs: macos: needs: tarball + if: ${{ ! needs.tarball.outputs.skip }} uses: ./.github/workflows/tarball-macos.yml with: archname: snapshot-${{ needs.tarball.outputs.branch }} @@ -71,6 +84,7 @@ jobs: windows: needs: tarball + if: ${{ ! needs.tarball.outputs.skip }} uses: ./.github/workflows/tarball-windows.yml with: archname: snapshot-${{ needs.tarball.outputs.branch }} From 4e8dbfdc1b5468cd7b97a38ade09346ed3dc9230 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 23 May 2026 14:43:09 +0900 Subject: [PATCH 3/6] Move the test for [Bug #14635] Float-specific test should be in test_float.rb. --- numeric.c | 2 +- test/ruby/test_float.rb | 8 ++++++++ test/ruby/test_numeric.rb | 8 -------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/numeric.c b/numeric.c index 175bd7cfa0f730..60b6adab90d6cb 100644 --- a/numeric.c +++ b/numeric.c @@ -2490,7 +2490,7 @@ flo_round(int argc, VALUE *argv, VALUE num) frexp(number, &binexp); if (float_round_overflow(ndigits, binexp)) return num; if (float_round_underflow(ndigits, binexp)) return DBL2NUM(0); - if (ndigits > 14) { + if (ndigits >= DBL_DIG) { /* In this case, pow(10, ndigits) may not be accurate. */ return rb_flo_round_by_rational(argc, argv, num); } diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb index d0d180593ab272..db811bf3712b7b 100644 --- a/test/ruby/test_float.rb +++ b/test/ruby/test_float.rb @@ -492,6 +492,14 @@ def (prec = Object.new).to_int; 2; end assert_equal(-1.26, -1.255.round(2)) end + def test_round_ndigits + bug14635 = "[ruby-core:86323]" + f = 0.5 + 31.times do |i| + assert_equal(0.5, f.round(i+1), bug14635 + " (argument: #{i+1})") + end + end + def test_round_half_even_with_precision assert_equal(767573.18759, 767573.1875850001.round(5, half: :even)) assert_equal(767573.18758, 767573.187585.round(5, half: :even)) diff --git a/test/ruby/test_numeric.rb b/test/ruby/test_numeric.rb index 35496ac875815a..b272b899211530 100644 --- a/test/ruby/test_numeric.rb +++ b/test/ruby/test_numeric.rb @@ -206,14 +206,6 @@ def test_cmp assert_nil(a <=> :foo) end - def test_float_round_ndigits - bug14635 = "[ruby-core:86323]" - f = 0.5 - 31.times do |i| - assert_equal(0.5, f.round(i+1), bug14635 + " (argument: #{i+1})") - end - end - def test_floor_ceil_round_truncate a = Class.new(Numeric) do def to_f; 1.5; end From 97ec327efc74b0df42421fa4344d1f05452d0623 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 23 May 2026 15:33:23 +0900 Subject: [PATCH 4/6] Refine `rb_flo_round_by_rational` Use already broken down arguments. --- internal/rational.h | 2 +- numeric.c | 7 ++++--- rational.c | 20 +++++++++++++++----- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/internal/rational.h b/internal/rational.h index f7e382af8cf8a5..1e53f883138988 100644 --- a/internal/rational.h +++ b/internal/rational.h @@ -38,7 +38,7 @@ VALUE rb_rational_cmp(VALUE self, VALUE other); VALUE rb_rational_pow(VALUE self, VALUE other); VALUE rb_rational_floor(VALUE self, int ndigits); VALUE rb_numeric_quo(VALUE x, VALUE y); -VALUE rb_flo_round_by_rational(int argc, VALUE *argv, VALUE num); +VALUE rb_flo_round_by_rational(VALUE num, int ndigits, enum ruby_num_rounding_mode mode); VALUE rb_float_numerator(VALUE x); VALUE rb_float_denominator(VALUE x); diff --git a/numeric.c b/numeric.c index 60b6adab90d6cb..50a5044f340023 100644 --- a/numeric.c +++ b/numeric.c @@ -75,6 +75,8 @@ #define DBL_EPSILON 2.2204460492503131e-16 #endif +#define ACCURATE_POW10(ndigits) ((ndigits) < DBL_DIG) + #ifndef USE_RB_INFINITY #elif !defined(WORDS_BIGENDIAN) /* BYTE_ORDER == LITTLE_ENDIAN */ const union bytesequence4_or_float rb_infinity = {{0x00, 0x00, 0x80, 0x7f}}; @@ -2490,9 +2492,8 @@ flo_round(int argc, VALUE *argv, VALUE num) frexp(number, &binexp); if (float_round_overflow(ndigits, binexp)) return num; if (float_round_underflow(ndigits, binexp)) return DBL2NUM(0); - if (ndigits >= DBL_DIG) { - /* In this case, pow(10, ndigits) may not be accurate. */ - return rb_flo_round_by_rational(argc, argv, num); + if (!ACCURATE_POW10(ndigits)) { + return rb_flo_round_by_rational(num, ndigits, mode); } f = pow(10, ndigits); x = ROUND_CALL(mode, round, (number, f)); diff --git a/rational.c b/rational.c index d6214451b483db..53aaa34f80e8e8 100644 --- a/rational.c +++ b/rational.c @@ -1374,10 +1374,12 @@ nurat_round_half_even(VALUE self) return num; } +static VALUE f_round_n(VALUE self, VALUE n, VALUE (*func)(VALUE)) ; + static VALUE f_round_common(int argc, VALUE *argv, VALUE self, VALUE (*func)(VALUE)) { - VALUE n, b, s; + VALUE n; if (rb_check_arity(argc, 0, 1) == 0) return (*func)(self); @@ -1387,6 +1389,14 @@ f_round_common(int argc, VALUE *argv, VALUE self, VALUE (*func)(VALUE)) if (!k_integer_p(n)) rb_raise(rb_eTypeError, "not an integer"); + return f_round_n(self, n, func); +} + +static VALUE +f_round_n(VALUE self, VALUE n, VALUE (*func)(VALUE)) +{ + VALUE b, s; + b = f_expt10(n); s = rb_rational_mul(self, b); @@ -1417,8 +1427,7 @@ rb_rational_floor(VALUE self, int ndigits) return nurat_floor(self); } else { - VALUE n = INT2NUM(ndigits); - return f_round_common(1, &n, self, nurat_floor); + return f_round_n(self, INT2NUM(ndigits), nurat_floor); } } @@ -1561,9 +1570,10 @@ nurat_round_n(int argc, VALUE *argv, VALUE self) } VALUE -rb_flo_round_by_rational(int argc, VALUE *argv, VALUE num) +rb_flo_round_by_rational(VALUE num, int ndigits, enum ruby_num_rounding_mode mode) { - return nurat_to_f(nurat_round_n(argc, argv, float_to_r(num))); + VALUE (*round_func)(VALUE) = ROUND_FUNC(mode, nurat_round); + return nurat_to_f(f_round_n(float_to_r(num), INT2NUM(ndigits), round_func)); } static double From 09dacb9c3d61264f3eb3731e2c65feb869130228 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 23 May 2026 16:07:56 +0900 Subject: [PATCH 5/6] [Bug #22079] Fix `Float#ceil` and `Float#floor` edge cases --- internal/rational.h | 2 ++ numeric.c | 6 ++++++ rational.c | 12 ++++++++++++ test/ruby/test_float.rb | 31 +++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/internal/rational.h b/internal/rational.h index 1e53f883138988..6861a901304bb6 100644 --- a/internal/rational.h +++ b/internal/rational.h @@ -39,6 +39,8 @@ VALUE rb_rational_pow(VALUE self, VALUE other); VALUE rb_rational_floor(VALUE self, int ndigits); VALUE rb_numeric_quo(VALUE x, VALUE y); VALUE rb_flo_round_by_rational(VALUE num, int ndigits, enum ruby_num_rounding_mode mode); +VALUE rb_flo_ceil_by_rational(VALUE num, int ndigits); +VALUE rb_flo_floor_by_rational(VALUE num, int ndigits); VALUE rb_float_numerator(VALUE x); VALUE rb_float_denominator(VALUE x); diff --git a/numeric.c b/numeric.c index 50a5044f340023..f4dc0f8927baed 100644 --- a/numeric.c +++ b/numeric.c @@ -2020,6 +2020,9 @@ rb_float_floor(VALUE num, int ndigits) if (float_round_overflow(ndigits, binexp)) return num; if (number > 0.0 && float_round_underflow(ndigits, binexp)) return DBL2NUM(0.0); + if (!ACCURATE_POW10(ndigits)) { + return rb_flo_floor_by_rational(num, ndigits); + } f = pow(10, ndigits); mul = floor(number * f); res = (mul + 1) / f; @@ -2228,6 +2231,9 @@ rb_float_ceil(VALUE num, int ndigits) if (float_round_overflow(ndigits, binexp)) return num; if (number < 0.0 && float_round_underflow(ndigits, binexp)) return DBL2NUM(0.0); + if (!ACCURATE_POW10(ndigits)) { + return rb_flo_ceil_by_rational(num, ndigits); + } f = pow(10, ndigits); f = ceil(number * f) / f; return DBL2NUM(f); diff --git a/rational.c b/rational.c index 53aaa34f80e8e8..b031838d6968c4 100644 --- a/rational.c +++ b/rational.c @@ -1576,6 +1576,18 @@ rb_flo_round_by_rational(VALUE num, int ndigits, enum ruby_num_rounding_mode mod return nurat_to_f(f_round_n(float_to_r(num), INT2NUM(ndigits), round_func)); } +VALUE +rb_flo_ceil_by_rational(VALUE num, int ndigits) +{ + return nurat_to_f(f_round_n(float_to_r(num), INT2NUM(ndigits), nurat_ceil)); +} + +VALUE +rb_flo_floor_by_rational(VALUE num, int ndigits) +{ + return nurat_to_f(f_round_n(float_to_r(num), INT2NUM(ndigits), nurat_floor)); +} + static double nurat_to_double(VALUE self) { diff --git a/test/ruby/test_float.rb b/test/ruby/test_float.rb index db811bf3712b7b..c01e8bb80b02b2 100644 --- a/test/ruby/test_float.rb +++ b/test/ruby/test_float.rb @@ -500,6 +500,14 @@ def test_round_ndigits end end + def test_round_with_precision_min + (0..3).each do |n| + n -= Float::MIN_10_EXP + f = Float::MIN.round(n) + assert_include([Float::MIN.floor(n), Float::MIN.ceil(n)], f, "round(#{n})") + end + end + def test_round_half_even_with_precision assert_equal(767573.18759, 767573.1875850001.round(5, half: :even)) assert_equal(767573.18758, 767573.187585.round(5, half: :even)) @@ -544,6 +552,16 @@ def (prec = Object.new).to_int; 2; end assert_equal(-100000000000000000000000000000000000000000000000000, -1.0.floor(-50), "[Bug #20654]") end + def test_floor_with_precision_min + min = Float::MIN + (0..3).each do |n| + n -= Float::MIN_10_EXP + f = min.floor(n) + assert_operator(f, :<=, Float::MIN, "floor(#{n})") + assert_operator(f, :>=, Float::MIN.floor(n-1), "ceil(#{n})") + end + end + def test_ceil_with_precision assert_equal(+0.1, +0.001.ceil(1)) assert_equal(-0.0, -0.001.ceil(1)) @@ -575,6 +593,19 @@ def (prec = Object.new).to_int; 2; end assert_equal(100000000000000000000000000000000000000000000000000, 1.0.ceil(-50), "[Bug #20654]") end + def test_ceil_with_precision_min + min = Float::MIN + (-Float::MIN_10_EXP).times do |n| + assert_equal(10.pow(-n), min.ceil(n)) + end + (0..3).each do |n| + n -= Float::MIN_10_EXP + f = min.ceil(n) + assert_operator(f, :>=, Float::MIN, "ceil(#{n})") + assert_operator(f, :<=, Float::MIN.ceil(n-1), "ceil(#{n})") + end + end + def test_truncate_with_precision assert_equal(1.100, 1.111.truncate(1)) assert_equal(1.110, 1.111.truncate(2)) From 3d137cf5fda54e6d208030f64c8dcd2b013ad52a Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sun, 14 Apr 2024 13:44:58 +0900 Subject: [PATCH 6/6] Remove duplicate GC guard Now there is a guard also in `execarg_parent_end` since commit:9ae6ee5a59e74e629f73222938b53a6eae8d2ded. --- process.c | 1 - 1 file changed, 1 deletion(-) diff --git a/process.c b/process.c index 126e36ee8d0d2a..cef1b09bd0c7de 100644 --- a/process.c +++ b/process.c @@ -2888,7 +2888,6 @@ void rb_execarg_parent_end(VALUE execarg_obj) { execarg_parent_end(execarg_obj); - RB_GC_GUARD(execarg_obj); } static void