summaryrefslogtreecommitdiffhomepage
path: root/mrbgems/mruby-rational/src/rational.c
diff options
context:
space:
mode:
Diffstat (limited to 'mrbgems/mruby-rational/src/rational.c')
-rw-r--r--mrbgems/mruby-rational/src/rational.c152
1 files changed, 117 insertions, 35 deletions
diff --git a/mrbgems/mruby-rational/src/rational.c b/mrbgems/mruby-rational/src/rational.c
index 676a1dc82..d5dd7adc4 100644
--- a/mrbgems/mruby-rational/src/rational.c
+++ b/mrbgems/mruby-rational/src/rational.c
@@ -60,83 +60,128 @@ static mrb_value
rational_numerator(mrb_state *mrb, mrb_value self)
{
struct mrb_rational *p = rational_ptr(mrb, self);
- return mrb_fixnum_value(p->numerator);
+ return mrb_int_value(mrb, p->numerator);
}
static mrb_value
rational_denominator(mrb_state *mrb, mrb_value self)
{
struct mrb_rational *p = rational_ptr(mrb, self);
- return mrb_fixnum_value(p->denominator);
+ return mrb_int_value(mrb, p->denominator);
}
static mrb_value
rational_new(mrb_state *mrb, mrb_int numerator, mrb_int denominator)
{
- struct RClass *c = mrb_class_get(mrb, "Rational");
+ struct RClass *c = mrb_class_get_id(mrb, MRB_SYM(Rational));
struct mrb_rational *p;
struct RBasic *rat = rational_alloc(mrb, c, &p);
+ if (denominator < 0) {
+ numerator *= -1;
+ denominator *= -1;
+ }
p->numerator = numerator;
p->denominator = denominator;
MRB_SET_FROZEN_FLAG(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 = (mrb_int)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
rational_s_new(mrb_state *mrb, mrb_value self)
{
mrb_int numerator, denominator;
-#ifdef MRB_WITHOUT_FLOAT
+#ifdef MRB_NO_FLOAT
mrb_get_args(mrb, "ii", &numerator, &denominator);
#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);
- if (mrb_fixnum_p(numv)) {
- numerator = mrb_fixnum(numv);
+ if (mrb_integer_p(numv)) {
+ numerator = mrb_integer(numv);
- if (mrb_fixnum_p(denomv)) {
- denominator = mrb_fixnum(denomv);
+ if (mrb_integer_p(denomv)) {
+ denominator = mrb_integer(denomv);
}
else {
+ mrb_float numf = (mrb_float)numerator;
mrb_float denomf = mrb_to_flo(mrb, denomv);
- DROP_PRECISION(denomf, numerator, denomf);
- denominator = (mrb_int)denomf;
+ return rational_new_f(mrb, numf/denomf);
}
}
else {
mrb_float numf = mrb_to_flo(mrb, numv);
+ mrb_float denomf;
- if (mrb_fixnum_p(denomv)) {
- denominator = mrb_fixnum(denomv);
+ if (mrb_integer_p(denomv)) {
+ denomf = (mrb_float)mrb_integer(denomv);
}
else {
- mrb_float denomf = mrb_to_flo(mrb, denomv);
-
- DROP_PRECISION(denomf, numf, denomf);
- denominator = (mrb_int)denomf;
+ denomf = mrb_to_flo(mrb, denomv);
}
-
- DROP_PRECISION(numf, numf, denominator);
- numerator = (mrb_int)numf;
+ return rational_new_f(mrb, numf/denomf);
}
#endif
-
return rational_new(mrb, numerator, denominator);
}
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
static mrb_value
rational_to_f(mrb_state *mrb, mrb_value self)
{
@@ -154,7 +199,7 @@ rational_to_i(mrb_state *mrb, mrb_value self)
if (p->denominator == 0) {
mrb_raise(mrb, mrb->eStandardError_class, "divided by 0");
}
- return mrb_fixnum_value(p->numerator / p->denominator);
+ return mrb_int_value(mrb, p->numerator / p->denominator);
}
static mrb_value
@@ -176,14 +221,50 @@ rational_negative_p(mrb_state *mrb, mrb_value self)
static mrb_value
fix_to_r(mrb_state *mrb, mrb_value self)
{
- return rational_new(mrb, mrb_fixnum(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)
{
struct RClass *rat;
- rat = mrb_define_class(mrb, "Rational", mrb_class_get(mrb, "Numeric"));
+ rat = mrb_define_class_id(mrb, MRB_SYM(Rational), mrb_class_get_id(mrb, MRB_SYM(Numeric)));
#ifdef RATIONAL_USE_ISTRUCT
MRB_SET_INSTANCE_TT(rat, MRB_TT_ISTRUCT);
mrb_assert(sizeof(struct mrb_rational) < ISTRUCT_DATA_SIZE);
@@ -194,13 +275,14 @@ void mrb_mruby_rational_gem_init(mrb_state *mrb)
mrb_define_class_method(mrb, rat, "_new", rational_s_new, MRB_ARGS_REQ(2));
mrb_define_method(mrb, rat, "numerator", rational_numerator, MRB_ARGS_NONE());
mrb_define_method(mrb, rat, "denominator", rational_denominator, MRB_ARGS_NONE());
-#ifndef MRB_WITHOUT_FLOAT
+#ifndef MRB_NO_FLOAT
mrb_define_method(mrb, rat, "to_f", rational_to_f, MRB_ARGS_NONE());
#endif
mrb_define_method(mrb, rat, "to_i", rational_to_i, 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, mrb->fixnum_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