From 97ec327efc74b0df42421fa4344d1f05452d0623 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Sat, 23 May 2026 15:33:23 +0900 Subject: [PATCH] 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 f7e382af8c..1e53f88313 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 60b6adab90..50a5044f34 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 d6214451b4..53aaa34f80 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