Made `Rational` overhaul.

- Implement `Rational()` in `C`.
- Use `float` to `rational` conversion function taken from:
  https://rosettacode.org/wiki/Convert_decimal_number_to_rational#C
parent f9e781d8
...@@ -82,13 +82,6 @@ class Numeric ...@@ -82,13 +82,6 @@ class Numeric
end end
module Kernel module Kernel
def Rational(numerator, denominator = 1)
a = numerator
b = denominator
a, b = b, a % b until b == 0
Rational._new(numerator.div(a), denominator.div(a))
end
[:+, :-, :*, :/, :<=>, :==, :<, :<=, :>, :>=].each do |op| [:+, :-, :*, :/, :<=>, :==, :<, :<=, :>, :>=].each do |op|
original_operator_name = :"__original_operator_#{op}_rational" original_operator_name = :"__original_operator_#{op}_rational"
Integer.instance_eval do Integer.instance_eval do
......
...@@ -76,12 +76,70 @@ rational_new(mrb_state *mrb, mrb_int numerator, mrb_int denominator) ...@@ -76,12 +76,70 @@ rational_new(mrb_state *mrb, mrb_int numerator, mrb_int denominator)
struct RClass *c = mrb_class_get_id(mrb, MRB_SYM(Rational)); struct RClass *c = mrb_class_get_id(mrb, MRB_SYM(Rational));
struct mrb_rational *p; struct mrb_rational *p;
struct RBasic *rat = rational_alloc(mrb, c, &p); struct RBasic *rat = rational_alloc(mrb, c, &p);
if (denominator < 0) {
numerator *= -1;
denominator *= -1;
}
p->numerator = numerator; p->numerator = numerator;
p->denominator = denominator; p->denominator = denominator;
MRB_SET_FROZEN_FLAG(rat); MRB_SET_FROZEN_FLAG(rat);
return mrb_obj_value(rat); return mrb_obj_value(rat);
} }
#ifndef MRB_NO_FLOAT
#include <math.h>
/* f : number to convert.
* num, denom: returned parts of the rational.
* md: max denominator value. Note that machine floating point number
* has a finite resolution (10e-16 ish for 64 bit double), so specifying
* a "best match with minimal error" is often wrong, because one can
* always just retrieve the significand and return that divided by
* 2**52, which is in a sense accurate, but generally not very useful:
* 1.0/7.0 would be "2573485501354569/18014398509481984", for example.
*/
#ifdef MRB_INT32
typedef float rat_float;
#else
typedef double rat_float;
#endif
static mrb_value
rational_new_f(mrb_state *mrb, mrb_float f0)
{
rat_float f = (rat_float)f0;
mrb_int md = 1000000;
/* a: continued fraction coefficients. */
mrb_int a, h[3] = { 0, 1, 0 }, k[3] = { 1, 0, 0 };
mrb_int x, d;
int64_t n = 1;
int i, neg = 0;
if (f < 0) { neg = 1; f = -f; }
while (f != floor(f)) { n <<= 1; f *= 2; }
d = f;
/* continued fraction and check denominator each step */
for (i = 0; i < 64; i++) {
a = n ? d / n : 0;
if (i && !a) break;
x = d; d = n; n = x % n;
x = a;
if (k[1] * a + k[0] >= md) {
x = (md - k[0]) / k[1];
if (x * 2 >= a || k[1] >= md)
i = 65;
else
break;
}
h[2] = x * h[1] + h[0]; h[0] = h[1]; h[1] = h[2];
k[2] = x * k[1] + k[0]; k[0] = k[1]; k[1] = k[2];
}
return rational_new(mrb, (neg ? -h[1] : h[1]), k[1]);
}
#endif
static mrb_value static mrb_value
rational_s_new(mrb_state *mrb, mrb_value self) rational_s_new(mrb_state *mrb, mrb_value self)
{ {
...@@ -91,14 +149,6 @@ rational_s_new(mrb_state *mrb, mrb_value self) ...@@ -91,14 +149,6 @@ rational_s_new(mrb_state *mrb, mrb_value self)
mrb_get_args(mrb, "ii", &numerator, &denominator); mrb_get_args(mrb, "ii", &numerator, &denominator);
#else #else
#define DROP_PRECISION(f, num, denom) \
do { \
while (f < (mrb_float)MRB_INT_MIN || f > (mrb_float)MRB_INT_MAX) { \
num /= 2; \
denom /= 2; \
} \
} while (0)
mrb_value numv, denomv; mrb_value numv, denomv;
mrb_get_args(mrb, "oo", &numv, &denomv); mrb_get_args(mrb, "oo", &numv, &denomv);
...@@ -112,9 +162,7 @@ rational_s_new(mrb_state *mrb, mrb_value self) ...@@ -112,9 +162,7 @@ rational_s_new(mrb_state *mrb, mrb_value self)
mrb_float numf = (mrb_float)numerator; mrb_float numf = (mrb_float)numerator;
mrb_float denomf = mrb_to_flo(mrb, denomv); mrb_float denomf = mrb_to_flo(mrb, denomv);
DROP_PRECISION(denomf, numf, denomf); return rational_new_f(mrb, numf/denomf);
numerator = (mrb_int)numf;
denominator = (mrb_int)denomf;
} }
} }
else { else {
...@@ -127,13 +175,9 @@ rational_s_new(mrb_state *mrb, mrb_value self) ...@@ -127,13 +175,9 @@ rational_s_new(mrb_state *mrb, mrb_value self)
else { else {
denomf = mrb_to_flo(mrb, denomv); denomf = mrb_to_flo(mrb, denomv);
} }
DROP_PRECISION(denomf, numf, denomf); return rational_new_f(mrb, numf/denomf);
DROP_PRECISION(numf, numf, denomf);
denominator = (mrb_int)denomf;
numerator = (mrb_int)numf;
} }
#endif #endif
return rational_new(mrb, numerator, denominator); return rational_new(mrb, numerator, denominator);
} }
...@@ -180,6 +224,42 @@ fix_to_r(mrb_state *mrb, mrb_value self) ...@@ -180,6 +224,42 @@ fix_to_r(mrb_state *mrb, mrb_value self)
return rational_new(mrb, mrb_integer(self), 1); return rational_new(mrb, mrb_integer(self), 1);
} }
static mrb_value
rational_m_int(mrb_state *mrb, mrb_int n, mrb_int d)
{
mrb_int a, b;
a = n;
b = d;
while (b != 0) {
mrb_int tmp = b;
b = a % b;
a = tmp;
}
return rational_new(mrb, n/a, d/a);
}
static mrb_value
rational_m(mrb_state *mrb, mrb_value self)
{
#ifdef MRB_NO_FLOAT
mrb_int n, d = 1;
mrb_get_args(mrb, "i|i", &n, &d);
return rational_m_int(mrb, n, d);
#else
mrb_value a, b = mrb_fixnum_value(1);
mrb_get_args(mrb, "o|o", &a, &b);
if (mrb_integer_p(a) && mrb_integer_p(b)) {
return rational_m_int(mrb, mrb_integer(a), mrb_integer(b));
}
else {
mrb_float x = mrb_to_flo(mrb, a);
mrb_float y = mrb_to_flo(mrb, b);
return rational_new_f(mrb, x/y);
}
#endif
}
void mrb_mruby_rational_gem_init(mrb_state *mrb) void mrb_mruby_rational_gem_init(mrb_state *mrb)
{ {
struct RClass *rat; struct RClass *rat;
...@@ -202,6 +282,7 @@ void mrb_mruby_rational_gem_init(mrb_state *mrb) ...@@ -202,6 +282,7 @@ void mrb_mruby_rational_gem_init(mrb_state *mrb)
mrb_define_method(mrb, rat, "to_r", rational_to_r, MRB_ARGS_NONE()); mrb_define_method(mrb, rat, "to_r", rational_to_r, MRB_ARGS_NONE());
mrb_define_method(mrb, rat, "negative?", rational_negative_p, MRB_ARGS_NONE()); mrb_define_method(mrb, rat, "negative?", rational_negative_p, MRB_ARGS_NONE());
mrb_define_method(mrb, mrb->integer_class, "to_r", fix_to_r, MRB_ARGS_NONE()); mrb_define_method(mrb, mrb->integer_class, "to_r", fix_to_r, MRB_ARGS_NONE());
mrb_define_method(mrb, mrb->kernel_module, "Rational", rational_m, MRB_ARGS_ARG(1,1));
} }
void void
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment