diff options
Diffstat (limited to 'rational.c')
| -rw-r--r-- | rational.c | 335 |
1 files changed, 165 insertions, 170 deletions
diff --git a/rational.c b/rational.c index e537bd498b..b031838d69 100644 --- a/rational.c +++ b/rational.c @@ -22,20 +22,27 @@ # define USE_GMP 0 #endif #endif -#if USE_GMP -#include <gmp.h> -#endif #include "id.h" #include "internal.h" #include "internal/array.h" #include "internal/complex.h" +#include "internal/error.h" #include "internal/gc.h" #include "internal/numeric.h" #include "internal/object.h" #include "internal/rational.h" #include "ruby_assert.h" +#if USE_GMP +RBIMPL_WARNING_PUSH() +# ifdef _MSC_VER +RBIMPL_WARNING_IGNORED(4146) /* for mpn_neg() */ +# endif +# include <gmp.h> +RBIMPL_WARNING_POP() +#endif + #define ZERO INT2FIX(0) #define ONE INT2FIX(1) #define TWO INT2FIX(2) @@ -169,22 +176,6 @@ f_idiv(VALUE x, VALUE y) #define f_expt10(x) rb_int_pow(INT2FIX(10), x) inline static int -f_zero_p(VALUE x) -{ - if (RB_INTEGER_TYPE_P(x)) { - return FIXNUM_ZERO_P(x); - } - else if (RB_TYPE_P(x, T_RATIONAL)) { - VALUE num = RRATIONAL(x)->num; - - return FIXNUM_ZERO_P(num); - } - return (int)rb_equal(x, ZERO); -} - -#define f_nonzero_p(x) (!f_zero_p(x)) - -inline static int f_one_p(VALUE x) { if (RB_INTEGER_TYPE_P(x)) { @@ -389,8 +380,8 @@ f_gcd(VALUE x, VALUE y) { VALUE r = f_gcd_orig(x, y); if (f_nonzero_p(r)) { - assert(f_zero_p(f_mod(x, r))); - assert(f_zero_p(f_mod(y, r))); + RUBY_ASSERT(f_zero_p(f_mod(x, r))); + RUBY_ASSERT(f_zero_p(f_mod(y, r))); } return r; } @@ -413,11 +404,11 @@ f_lcm(VALUE x, VALUE y) inline static VALUE nurat_s_new_internal(VALUE klass, VALUE num, VALUE den) { - NEWOBJ_OF(obj, struct RRational, klass, T_RATIONAL | (RGENGC_WB_PROTECTED_RATIONAL ? FL_WB_PROTECTED : 0)); + NEWOBJ_OF(obj, struct RRational, klass, T_RATIONAL, sizeof(struct RRational)); RATIONAL_SET_NUM((VALUE)obj, num); RATIONAL_SET_DEN((VALUE)obj, den); - OBJ_FREEZE_RAW((VALUE)obj); + OBJ_FREEZE((VALUE)obj); return (VALUE)obj; } @@ -455,8 +446,8 @@ nurat_int_value(VALUE num) static void nurat_canonicalize(VALUE *num, VALUE *den) { - assert(num); assert(RB_INTEGER_TYPE_P(*num)); - assert(den); assert(RB_INTEGER_TYPE_P(*den)); + RUBY_ASSERT(num); RUBY_ASSERT(RB_INTEGER_TYPE_P(*num)); + RUBY_ASSERT(den); RUBY_ASSERT(RB_INTEGER_TYPE_P(*den)); if (INT_NEGATIVE_P(*den)) { *num = rb_int_uminus(*num); *den = rb_int_uminus(*den); @@ -496,16 +487,16 @@ nurat_s_canonicalize_internal_no_reduce(VALUE klass, VALUE num, VALUE den) inline static VALUE f_rational_new2(VALUE klass, VALUE x, VALUE y) { - assert(!k_rational_p(x)); - assert(!k_rational_p(y)); + RUBY_ASSERT(!k_rational_p(x)); + RUBY_ASSERT(!k_rational_p(y)); return nurat_s_canonicalize_internal(klass, x, y); } inline static VALUE f_rational_new_no_reduce2(VALUE klass, VALUE x, VALUE y) { - assert(!k_rational_p(x)); - assert(!k_rational_p(y)); + RUBY_ASSERT(!k_rational_p(x)); + RUBY_ASSERT(!k_rational_p(y)); return nurat_s_canonicalize_internal_no_reduce(klass, x, y); } @@ -602,14 +593,18 @@ nurat_denominator(VALUE self) /* * call-seq: - * -rat -> rational + * -self -> rational + * + * Returns +self+, negated: + * + * -(1/3r) # => (-1/3) + * -(-1/3r) # => (1/3) * - * Negates +rat+. */ VALUE rb_rational_uminus(VALUE self) { - const int unused = (assert(RB_TYPE_P(self, T_RATIONAL)), 0); + const int unused = (RUBY_ASSERT(RB_TYPE_P(self, T_RATIONAL)), 0); get_dat1(self); (void)unused; return f_rational_new2(CLASS_OF(self), rb_int_uminus(dat->num), dat->den); @@ -645,7 +640,7 @@ inline static VALUE f_imul(long x, long y) { VALUE r = f_imul_orig(x, y); - assert(f_eqeq_p(r, f_mul(LONG2NUM(x), LONG2NUM(y)))); + RUBY_ASSERT(f_eqeq_p(r, f_mul(LONG2NUM(x), LONG2NUM(y)))); return r; } #endif @@ -708,16 +703,27 @@ f_addsub(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k) static double nurat_to_double(VALUE self); /* - * call-seq: - * rat + numeric -> numeric + * call-seq: + * self + other -> numeric + * + * Returns the sum of +self+ and +other+: + * + * Rational(2, 3) + 0 # => (2/3) + * Rational(2, 3) + 1 # => (5/3) + * Rational(2, 3) + -1 # => (-1/3) + * + * Rational(2, 3) + Complex(1, 0) # => ((5/3)+0i) + * + * Rational(2, 3) + Rational(1, 1) # => (5/3) + * Rational(2, 3) + Rational(3, 2) # => (13/6) + * Rational(2, 3) + Rational(3.0, 2.0) # => (13/6) + * Rational(2, 3) + Rational(3.1, 2.1) # => (30399297484750849/14186338826217063) + * + * For a computation involving Floats, the result may be inexact (see Float#+): * - * Performs addition. + * Rational(2, 3) + 1.0 # => 1.6666666666666665 + * Rational(2, 3) + Complex(1.0, 0.0) # => (1.6666666666666665+0.0i) * - * Rational(2, 3) + Rational(2, 3) #=> (4/3) - * Rational(900) + Rational(1) #=> (901/1) - * Rational(-2, 9) + Rational(-9, 2) #=> (-85/18) - * Rational(9, 8) + 4 #=> (41/8) - * Rational(20, 9) + 9.8 #=> 12.022222222222222 */ VALUE rb_rational_plus(VALUE self, VALUE other) @@ -750,9 +756,9 @@ rb_rational_plus(VALUE self, VALUE other) /* * call-seq: - * rat - numeric -> numeric + * self - other -> numeric * - * Performs subtraction. + * Returns the difference of +self+ and +other+: * * Rational(2, 3) - Rational(2, 3) #=> (0/1) * Rational(900) - Rational(1) #=> (899/1) @@ -794,7 +800,7 @@ f_muldiv(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k) { VALUE num, den; - assert(RB_TYPE_P(self, T_RATIONAL)); + RUBY_ASSERT(RB_TYPE_P(self, T_RATIONAL)); /* Integer#** can return Rational with Float right now */ if (RB_FLOAT_TYPE_P(anum) || RB_FLOAT_TYPE_P(aden) || @@ -805,10 +811,10 @@ f_muldiv(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k) return DBL2NUM(x); } - assert(RB_INTEGER_TYPE_P(anum)); - assert(RB_INTEGER_TYPE_P(aden)); - assert(RB_INTEGER_TYPE_P(bnum)); - assert(RB_INTEGER_TYPE_P(bden)); + RUBY_ASSERT(RB_INTEGER_TYPE_P(anum)); + RUBY_ASSERT(RB_INTEGER_TYPE_P(aden)); + RUBY_ASSERT(RB_INTEGER_TYPE_P(bnum)); + RUBY_ASSERT(RB_INTEGER_TYPE_P(bden)); if (k == '/') { VALUE t; @@ -846,15 +852,17 @@ f_muldiv(VALUE self, VALUE anum, VALUE aden, VALUE bnum, VALUE bden, int k) /* * call-seq: - * rat * numeric -> numeric + * self * other -> numeric * - * Performs multiplication. + * Returns the numeric product of +self+ and +other+: + * + * Rational(9, 8) * 4 #=> (9/2) + * Rational(20, 9) * 9.8 #=> 21.77777777777778 + * Rational(9, 8) * Complex(1, 2) # => ((9/8)+(9/4)*i) + * Rational(2, 3) * Rational(2, 3) #=> (4/9) + * Rational(900) * Rational(1) #=> (900/1) + * Rational(-2, 9) * Rational(-9, 2) #=> (1/1) * - * Rational(2, 3) * Rational(2, 3) #=> (4/9) - * Rational(900) * Rational(1) #=> (900/1) - * Rational(-2, 9) * Rational(-9, 2) #=> (1/1) - * Rational(9, 8) * 4 #=> (9/2) - * Rational(20, 9) * 9.8 #=> 21.77777777777778 */ VALUE rb_rational_mul(VALUE self, VALUE other) @@ -887,10 +895,9 @@ rb_rational_mul(VALUE self, VALUE other) /* * call-seq: - * rat / numeric -> numeric - * rat.quo(numeric) -> numeric + * self / other -> numeric * - * Performs division. + * Returns the quotient of +self+ and +other+: * * Rational(2, 3) / Rational(2, 3) #=> (1/1) * Rational(900) / Rational(1) #=> (900/1) @@ -946,8 +953,8 @@ rb_rational_div(VALUE self, VALUE other) * Rational(2, 3).fdiv(0.5) #=> 1.3333333333333333 * Rational(2).fdiv(3) #=> 0.6666666666666666 */ -static VALUE -nurat_fdiv(VALUE self, VALUE other) +VALUE +rb_rational_fdiv(VALUE self, VALUE other) { VALUE div; if (f_zero_p(other)) @@ -964,9 +971,9 @@ nurat_fdiv(VALUE self, VALUE other) /* * call-seq: - * rat ** numeric -> numeric + * self ** exponent -> numeric * - * Performs exponentiation. + * Returns +self+ raised to the power +exponent+: * * Rational(2) ** Rational(3) #=> (8/1) * Rational(10) ** -2 #=> (1/100) @@ -1041,8 +1048,7 @@ rb_rational_pow(VALUE self, VALUE other) } } else if (RB_BIGNUM_TYPE_P(other)) { - rb_warn("in a**b, b may be too big"); - return rb_float_pow(nurat_to_f(self), other); + rb_raise(rb_eArgError, "exponent is too large"); } else if (RB_FLOAT_TYPE_P(other) || RB_TYPE_P(other, T_RATIONAL)) { return rb_float_pow(nurat_to_f(self), other); @@ -1055,20 +1061,30 @@ rb_rational_pow(VALUE self, VALUE other) /* * call-seq: - * rational <=> numeric -> -1, 0, +1, or nil + * self <=> other -> -1, 0, 1, or nil + * + * Compares +self+ and +other+. + * + * Returns: + * + * - +-1+, if +self+ is less than +other+. + * - +0+, if the two values are the same. + * - +1+, if +self+ is greater than +other+. + * - +nil+, if the two values are incomparable. * - * Returns -1, 0, or +1 depending on whether +rational+ is - * less than, equal to, or greater than +numeric+. + * Examples: * - * +nil+ is returned if the two values are incomparable. + * Rational(2, 3) <=> Rational(4, 3) # => -1 + * Rational(2, 1) <=> Rational(2, 1) # => 0 + * Rational(2, 1) <=> 2 # => 0 + * Rational(2, 1) <=> 2.0 # => 0 + * Rational(2, 1) <=> Complex(2, 0) # => 0 + * Rational(4, 3) <=> Rational(2, 3) # => 1 + * Rational(4, 3) <=> :foo # => nil * - * Rational(2, 3) <=> Rational(2, 3) #=> 0 - * Rational(5) <=> 5 #=> 0 - * Rational(2, 3) <=> Rational(1, 3) #=> 1 - * Rational(1, 3) <=> 1 #=> -1 - * Rational(1, 3) <=> 0.3 #=> 1 + * \Class \Rational includes module Comparable, + * each of whose methods uses Rational#<=> for comparison. * - * Rational(1, 3) <=> "0.3" #=> nil */ VALUE rb_rational_cmp(VALUE self, VALUE other) @@ -1113,9 +1129,9 @@ rb_rational_cmp(VALUE self, VALUE other) /* * call-seq: - * rat == object -> true or false + * self == other -> true or false * - * Returns +true+ if +rat+ equals +object+ numerically. + * Returns whether +self+ and +other+ are numerically equal: * * Rational(2, 3) == Rational(2, 3) #=> true * Rational(5) == 5 #=> true @@ -1232,7 +1248,6 @@ nurat_negative_p(VALUE self) * (1/2r).abs #=> (1/2) * (-1/2r).abs #=> (1/2) * - * Rational#magnitude is an alias for Rational#abs. */ VALUE @@ -1359,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); @@ -1372,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); @@ -1402,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); } } @@ -1546,9 +1570,22 @@ 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) +{ + VALUE (*round_func)(VALUE) = ROUND_FUNC(mode, nurat_round); + 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(nurat_round_n(argc, argv, float_to_r(num))); + 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 @@ -1847,7 +1884,7 @@ nurat_loader(VALUE self, VALUE a) nurat_canonicalize(&num, &den); RATIONAL_SET_NUM((VALUE)dat, num); RATIONAL_SET_DEN((VALUE)dat, den); - OBJ_FREEZE_RAW(self); + OBJ_FREEZE(self); return self; } @@ -2061,30 +2098,6 @@ rb_rational_canonicalize(VALUE x) /* * call-seq: - * int.numerator -> self - * - * Returns self. - */ -static VALUE -integer_numerator(VALUE self) -{ - return self; -} - -/* - * call-seq: - * int.denominator -> 1 - * - * Returns 1. - */ -static VALUE -integer_denominator(VALUE self) -{ - return INT2FIX(1); -} - -/* - * call-seq: * flo.numerator -> integer * * Returns the numerator. The result is machine dependent. @@ -2128,32 +2141,6 @@ rb_float_denominator(VALUE self) /* * call-seq: - * nil.to_r -> (0/1) - * - * Returns zero as a rational. - */ -static VALUE -nilclass_to_r(VALUE self) -{ - return rb_rational_new1(INT2FIX(0)); -} - -/* - * call-seq: - * nil.rationalize([eps]) -> (0/1) - * - * Returns zero as a rational. The optional argument +eps+ is always - * ignored. - */ -static VALUE -nilclass_rationalize(int argc, VALUE *argv, VALUE self) -{ - rb_check_arity(argc, 0, 1); - return nilclass_to_r(self); -} - -/* - * call-seq: * int.to_r -> rational * * Returns the value as a rational. @@ -2339,6 +2326,12 @@ islettere(int c) return (c == 'e' || c == 'E'); } +inline static int +isletterr(int c) +{ + return (c == 'r' || c == 'R'); +} + static VALUE negate_num(VALUE num) { @@ -2388,7 +2381,13 @@ read_num(const char **s, const char *const end, VALUE *num, VALUE *nexp) ok = 1; } - if (ok && *s + 1 < end && islettere(**s)) { + if (!ok || *s >= end) { + /* failed or finish */ + } + else if (isletterr(**s)) { + (*s)++; + } + else if (*s + 1 < end && islettere(**s)) { (*s)++; expsign = read_sign(s, end); exp = rb_int_parse_cstr(*s, end-*s, &e, NULL, @@ -2512,31 +2511,32 @@ string_to_r_strict(VALUE self, int raise) /* * call-seq: - * str.to_r -> rational - * - * Returns the result of interpreting leading characters in +str+ - * as a rational. Leading whitespace and extraneous characters - * past the end of a valid number are ignored. - * Digit sequences can be separated by an underscore. - * If there is not a valid number at the start of +str+, - * zero is returned. This method never raises an exception. - * - * ' 2 '.to_r #=> (2/1) - * '300/2'.to_r #=> (150/1) - * '-9.2'.to_r #=> (-46/5) - * '-9.2e2'.to_r #=> (-920/1) - * '1_234_567'.to_r #=> (1234567/1) - * '21 June 09'.to_r #=> (21/1) - * '21/06/09'.to_r #=> (7/2) - * 'BWV 1079'.to_r #=> (0/1) - * - * NOTE: "0.3".to_r isn't the same as 0.3.to_r. The former is - * equivalent to "3/10".to_r, but the latter isn't so. + * str.to_r -> rational * - * "0.3".to_r == 3/10r #=> true - * 0.3.to_r == 3/10r #=> false + * Returns the result of interpreting leading characters in +self+ as a rational value: + * + * '123'.to_r # => (123/1) # Integer literal. + * '300/2'.to_r # => (150/1) # Rational literal. + * '-9.2'.to_r # => (-46/5) # Float literal. + * '-9.2e2'.to_r # => (-920/1) # Float literal. + * + * Ignores leading and trailing whitespace, and trailing non-numeric characters: * - * See also Kernel#Rational. + * ' 2 '.to_r # => (2/1) + * '21-Jun-09'.to_r # => (21/1) + * + * Returns \Rational zero if there are no leading numeric characters. + * + * 'BWV 1079'.to_r # => (0/1) + * + * NOTE: <tt>'0.3'.to_r</tt> is equivalent to <tt>3/10r</tt>, + * but is different from <tt>0.3.to_r</tt>: + * + * '0.3'.to_r # => (3/10) + * 3/10r # => (3/10) + * 0.3.to_r # => (5404319552844595/18014398509481984) + * + * Related: see {Converting to Non-String}[rdoc-ref:String@Converting+to+Non--5CString]. */ static VALUE string_to_r(VALUE self) @@ -2576,11 +2576,11 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise) VALUE a1 = numv, a2 = denv; int state; - assert(a1 != Qundef); + RUBY_ASSERT(!UNDEF_P(a1)); if (NIL_P(a1) || NIL_P(a2)) { if (!raise) return Qnil; - rb_raise(rb_eTypeError, "can't convert nil into Rational"); + rb_cant_convert(Qnil, "Rational"); } if (RB_TYPE_P(a1, T_COMPLEX)) { @@ -2627,7 +2627,7 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise) a2 = string_to_r_strict(a2, raise); if (!raise && NIL_P(a2)) return Qnil; } - else if (a2 != Qundef && !rb_respond_to(a2, idTo_r)) { + else if (!UNDEF_P(a2) && !rb_respond_to(a2, idTo_r)) { VALUE tmp = rb_protect(rb_check_to_int, a2, NULL); rb_set_errinfo(Qnil); if (!NIL_P(tmp)) { @@ -2636,11 +2636,11 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise) } if (RB_TYPE_P(a1, T_RATIONAL)) { - if (a2 == Qundef || (k_exact_one_p(a2))) + if (UNDEF_P(a2) || (k_exact_one_p(a2))) return a1; } - if (a2 == Qundef) { + if (UNDEF_P(a2)) { if (!RB_INTEGER_TYPE_P(a1)) { if (!raise) { VALUE result = rb_protect(to_rational, a1, NULL); @@ -2690,7 +2690,7 @@ nurat_convert(VALUE klass, VALUE numv, VALUE denv, int raise) a1 = nurat_int_value(a1); - if (a2 == Qundef) { + if (UNDEF_P(a2)) { a2 = ONE; } else if (!k_integer_p(a2) && !raise) { @@ -2727,7 +2727,7 @@ nurat_s_convert(int argc, VALUE *argv, VALUE klass) * * You can convert certain objects to Rationals with: * - * - \Method #Rational. + * - Method #Rational. * * Examples * @@ -2791,7 +2791,7 @@ Init_Rational(void) rb_define_method(rb_cRational, "*", rb_rational_mul, 1); rb_define_method(rb_cRational, "/", rb_rational_div, 1); rb_define_method(rb_cRational, "quo", rb_rational_div, 1); - rb_define_method(rb_cRational, "fdiv", nurat_fdiv, 1); + rb_define_method(rb_cRational, "fdiv", rb_rational_fdiv, 1); rb_define_method(rb_cRational, "**", nurat_expt, 1); rb_define_method(rb_cRational, "<=>", rb_rational_cmp, 1); @@ -2832,14 +2832,9 @@ Init_Rational(void) rb_define_method(rb_cNumeric, "denominator", numeric_denominator, 0); rb_define_method(rb_cNumeric, "quo", rb_numeric_quo, 1); - rb_define_method(rb_cInteger, "numerator", integer_numerator, 0); - rb_define_method(rb_cInteger, "denominator", integer_denominator, 0); - rb_define_method(rb_cFloat, "numerator", rb_float_numerator, 0); rb_define_method(rb_cFloat, "denominator", rb_float_denominator, 0); - rb_define_method(rb_cNilClass, "to_r", nilclass_to_r, 0); - rb_define_method(rb_cNilClass, "rationalize", nilclass_rationalize, -1); rb_define_method(rb_cInteger, "to_r", integer_to_r, 0); rb_define_method(rb_cInteger, "rationalize", integer_rationalize, -1); rb_define_method(rb_cFloat, "to_r", float_to_r, 0); |
