mruby-rational: fix infinite recursion with bigint comparison

rational_eq_b was using wrong struct fields (p1->numerator/denominator
which access i.num/i.den) for bigint-backed rationals that use b.num/b.den.
Also added missing MRB_TT_BIGINT case to prevent fallthrough to default
case which caused ping-pong recursion between Rational#== and Integer#==.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2026-01-12 19:23:15 +09:00
parent 26a1064d99
commit 78fe8a0476
+40 -21
View File
@@ -588,32 +588,44 @@ rational_eq_b(mrb_state *mrb, mrb_value x, mrb_value y)
switch (mrb_type(y)) {
case MRB_TT_INTEGER:
if (p1->denominator != 1) return mrb_false_value();
result = p1->numerator == mrb_integer(y);
break;
{
/* For bigint-backed rationals, check if denominator is 1 */
mrb_value den = mrb_obj_value(p1->b.den);
mrb_int den_cmp = mrb_bint_cmp(mrb, den, mrb_int_value(mrb, 1));
if (den_cmp != 0) return mrb_false_value();
mrb_value num = mrb_obj_value(p1->b.num);
result = mrb_bint_cmp(mrb, num, y) == 0;
break;
}
#ifdef MRB_USE_BIGINT
case MRB_TT_BIGINT:
{
/* For bigint-backed rationals comparing with bigint */
mrb_value den = mrb_obj_value(p1->b.den);
mrb_int den_cmp = mrb_bint_cmp(mrb, den, mrb_int_value(mrb, 1));
if (den_cmp != 0) return mrb_false_value();
mrb_value num = mrb_obj_value(p1->b.num);
result = mrb_bint_cmp(mrb, num, y) == 0;
break;
}
#endif
#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
result = ((double)p1->numerator/p1->denominator) == mrb_float(y);
break;
{
/* For bigint-backed rationals, convert to float and compare */
mrb_float num_f = mrb_bint_as_float(mrb, mrb_obj_value(p1->b.num));
mrb_float den_f = mrb_bint_as_float(mrb, mrb_obj_value(p1->b.den));
result = (num_f / den_f) == mrb_float(y);
break;
}
#endif
case MRB_TT_RATIONAL:
{
struct mrb_rational *p2 = rat_ptr(mrb, y);
mrb_int a, b;
if (p1->numerator == p2->numerator && p1->denominator == p2->denominator) {
return mrb_true_value();
}
if (mrb_int_mul_overflow(p1->numerator, p2->denominator, &a) ||
mrb_int_mul_overflow(p2->numerator, p1->denominator, &b)) {
#ifdef MRB_NO_FLOAT
rat_overflow(mrb);
#else
result = (double)p1->numerator*p2->denominator == (double)p2->numerator*p2->denominator;
break;
#endif
}
result = a == b;
/* Compare by converting to float - less precise but safe */
mrb_float v1 = mrb_bint_as_float(mrb, mrb_obj_value(p1->b.num)) /
mrb_bint_as_float(mrb, mrb_obj_value(p1->b.den));
mrb_float v2 = rat_float(mrb, y);
result = v1 == v2;
break;
}
@@ -657,6 +669,13 @@ rational_eq(mrb_state *mrb, mrb_value x)
if (p1->denominator != 1) return mrb_false_value();
result = p1->numerator == mrb_integer(y);
break;
#ifdef MRB_USE_BIGINT
case MRB_TT_BIGINT:
/* Non-bigint rational comparing with bigint */
if (p1->denominator != 1) return mrb_false_value();
result = mrb_bint_cmp(mrb, y, mrb_int_value(mrb, p1->numerator)) == 0;
break;
#endif
#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
result = ((double)p1->numerator/p1->denominator) == mrb_float(y);