diff options
Diffstat (limited to 'numeric.c')
| -rw-r--r-- | numeric.c | 2398 |
1 files changed, 1455 insertions, 943 deletions
@@ -2,16 +2,16 @@ numeric.c - - $Author: matz $ - $Date: 2006/05/01 03:46:46 $ + $Author$ created at: Fri Aug 13 18:33:09 JST 1993 - Copyright (C) 1993-2003 Yukihiro Matsumoto + Copyright (C) 1993-2007 Yukihiro Matsumoto **********************************************************************/ -#include "ruby.h" -#include "env.h" +#include "ruby/ruby.h" +#include "ruby/encoding.h" +#include "ruby/util.h" #include <ctype.h> #include <math.h> #include <stdio.h> @@ -63,6 +63,44 @@ #define DBL_EPSILON 2.2204460492503131e-16 #endif +#ifdef HAVE_INFINITY +#elif BYTE_ORDER == LITTLE_ENDIAN +const unsigned char rb_infinity[] = "\x00\x00\x80\x7f"; +#else +const unsigned char rb_infinity[] = "\x7f\x80\x00\x00"; +#endif + +#ifdef HAVE_NAN +#elif BYTE_ORDER == LITTLE_ENDIAN +const unsigned char rb_nan[] = "\x00\x00\xc0\x7f"; +#else +const unsigned char rb_nan[] = "\x7f\xc0\x00\x00"; +#endif + +extern double round(double); + +#ifndef HAVE_ROUND +double +round(double x) +{ + double f; + + if (x > 0.0) { + f = floor(x); + x = f + (x - f >= 0.5); + } + else if (x < 0.0) { + f = ceil(x); + x = f - (f - x >= 0.5); + } + return x; +} +#endif + +static VALUE fix_uminus(VALUE num); +static VALUE fix_mul(VALUE x, VALUE y); +static VALUE int_pow(long x, unsigned long y); + static ID id_coerce, id_to_i, id_eq; VALUE rb_cNumeric; @@ -74,7 +112,7 @@ VALUE rb_eZeroDivError; VALUE rb_eFloatDomainError; void -rb_num_zerodiv() +rb_num_zerodiv(void) { rb_raise(rb_eZeroDivError, "divided by 0"); } @@ -82,54 +120,51 @@ rb_num_zerodiv() /* * call-seq: - * num.coerce(numeric) => array - * + * num.coerce(numeric) -> array + * * If <i>aNumeric</i> is the same type as <i>num</i>, returns an array * containing <i>aNumeric</i> and <i>num</i>. Otherwise, returns an * array with both <i>aNumeric</i> and <i>num</i> represented as * <code>Float</code> objects. This coercion mechanism is used by * Ruby to handle mixed-type numeric operations: it is intended to * find a compatible common type between the two operands of the operator. - * + * * 1.coerce(2.5) #=> [2.5, 1.0] * 1.2.coerce(3) #=> [3.0, 1.2] * 1.coerce(2) #=> [2, 1] */ static VALUE -num_coerce(x, y) - VALUE x, y; +num_coerce(VALUE x, VALUE y) { if (CLASS_OF(x) == CLASS_OF(y)) return rb_assoc_new(y, x); - return rb_assoc_new(rb_Float(y), rb_Float(x)); + x = rb_Float(x); + y = rb_Float(y); + return rb_assoc_new(y, x); } static VALUE -coerce_body(x) - VALUE *x; +coerce_body(VALUE *x) { return rb_funcall(x[1], id_coerce, 1, x[0]); } static VALUE -coerce_rescue(x) - VALUE *x; +coerce_rescue(VALUE *x) { volatile VALUE v = rb_inspect(x[1]); rb_raise(rb_eTypeError, "%s can't be coerced into %s", rb_special_const_p(x[1])? - RSTRING(v)->ptr: + RSTRING_PTR(v): rb_obj_classname(x[1]), rb_obj_classname(x[0])); return Qnil; /* dummy */ } static int -do_coerce(x, y, err) - VALUE *x, *y; - int err; +do_coerce(VALUE *x, VALUE *y, int err) { VALUE ary; VALUE a[2]; @@ -137,43 +172,40 @@ do_coerce(x, y, err) a[0] = *x; a[1] = *y; ary = rb_rescue(coerce_body, (VALUE)a, err?coerce_rescue:0, (VALUE)a); - if (TYPE(ary) != T_ARRAY || RARRAY(ary)->len != 2) { + if (TYPE(ary) != T_ARRAY || RARRAY_LEN(ary) != 2) { if (err) { rb_raise(rb_eTypeError, "coerce must return [x, y]"); } - return Qfalse; + return FALSE; } - *x = RARRAY(ary)->ptr[0]; - *y = RARRAY(ary)->ptr[1]; - return Qtrue; + *x = RARRAY_PTR(ary)[0]; + *y = RARRAY_PTR(ary)[1]; + return TRUE; } VALUE -rb_num_coerce_bin(x, y) - VALUE x, y; +rb_num_coerce_bin(VALUE x, VALUE y, ID func) { - do_coerce(&x, &y, Qtrue); - return rb_funcall(x, ruby_frame->orig_func, 1, y); + do_coerce(&x, &y, TRUE); + return rb_funcall(x, func, 1, y); } VALUE -rb_num_coerce_cmp(x, y) - VALUE x, y; +rb_num_coerce_cmp(VALUE x, VALUE y, ID func) { - if (do_coerce(&x, &y, Qfalse)) - return rb_funcall(x, ruby_frame->orig_func, 1, y); + if (do_coerce(&x, &y, FALSE)) + return rb_funcall(x, func, 1, y); return Qnil; } VALUE -rb_num_coerce_relop(x, y) - VALUE x, y; +rb_num_coerce_relop(VALUE x, VALUE y, ID func) { VALUE c, x0 = x, y0 = y; - if (!do_coerce(&x, &y, Qfalse) || - NIL_P(c = rb_funcall(x, ruby_frame->orig_func, 1, y))) { + if (!do_coerce(&x, &y, FALSE) || + NIL_P(c = rb_funcall(x, func, 1, y))) { rb_cmperr(x0, y0); return Qnil; /* not reached */ } @@ -186,22 +218,22 @@ rb_num_coerce_relop(x, y) */ static VALUE -num_sadded(x, name) - VALUE x, name; +num_sadded(VALUE x, VALUE name) { - ruby_frame = ruby_frame->prev; /* pop frame for "singleton_method_added" */ + ID mid = rb_to_id(name); + /* ruby_frame = ruby_frame->prev; */ /* pop frame for "singleton_method_added" */ /* Numerics should be values; singleton_methods should not be added to them */ + rb_remove_method_id(rb_singleton_class(x), mid); rb_raise(rb_eTypeError, "can't define singleton method \"%s\" for %s", - rb_id2name(rb_to_id(name)), - rb_obj_classname(x)); + rb_id2name(mid), + rb_obj_classname(x)); return Qnil; /* not reached */ } /* :nodoc: */ static VALUE -num_init_copy(x, y) - VALUE x, y; +num_init_copy(VALUE x, VALUE y) { /* Numerics are immutable values, which should not be copied */ rb_raise(rb_eTypeError, "can't copy %s", rb_obj_classname(x)); @@ -210,90 +242,162 @@ num_init_copy(x, y) /* * call-seq: - * +num => num - * + * +num -> num + * * Unary Plus---Returns the receiver's value. */ static VALUE -num_uplus(num) - VALUE num; +num_uplus(VALUE num) { return num; } /* * call-seq: - * -num => numeric - * + * num.i -> Complex(0,num) + * + * Returns the corresponding imaginary number. + * Not available for complex numbers. + */ + +static VALUE +num_imaginary(VALUE num) +{ + return rb_complex_new(INT2FIX(0), num); +} + + +/* + * call-seq: + * -num -> numeric + * * Unary Minus---Returns the receiver's value, negated. */ static VALUE -num_uminus(num) - VALUE num; +num_uminus(VALUE num) { VALUE zero; zero = INT2FIX(0); - do_coerce(&zero, &num, Qtrue); + do_coerce(&zero, &num, TRUE); return rb_funcall(zero, '-', 1, num); } /* * call-seq: - * num.quo(numeric) => result - * - * Equivalent to <code>Numeric#/</code>, but overridden in subclasses. + * num.quo(numeric) -> real + * + * Returns most exact division (rational for integers, float for floats). */ static VALUE -num_quo(x, y) - VALUE x, y; +num_quo(VALUE x, VALUE y) { - return rb_funcall(x, '/', 1, y); + return rb_funcall(rb_rational_raw1(x), '/', 1, y); } -static VALUE num_floor(VALUE num); +/* + * call-seq: + * num.fdiv(numeric) -> float + * + * Returns float division. + */ + +static VALUE +num_fdiv(VALUE x, VALUE y) +{ + return rb_funcall(rb_Float(x), '/', 1, y); +} + /* * call-seq: - * num.div(numeric) => integer - * + * num.div(numeric) -> integer + * * Uses <code>/</code> to perform division, then converts the result to - * an integer. <code>Numeric</code> does not define the <code>/</code> + * an integer. <code>numeric</code> does not define the <code>/</code> * operator; this is left to subclasses. + * + * Equivalent to + * <i>num</i>.<code>divmod(</code><i>aNumeric</i><code>)[0]</code>. + * + * See <code>Numeric#divmod</code>. */ static VALUE -num_div(x, y) - VALUE x, y; +num_div(VALUE x, VALUE y) { - return num_floor(rb_funcall(x, '/', 1, y)); + if (rb_equal(INT2FIX(0), y)) rb_num_zerodiv(); + return rb_funcall(rb_funcall(x, '/', 1, y), rb_intern("floor"), 0); } +/* + * call-seq: + * num.modulo(numeric) -> real + * + * x.modulo(y) means x-y*(x/y).floor + * + * Equivalent to + * <i>num</i>.<code>divmod(</code><i>aNumeric</i><code>)[1]</code>. + * + * See <code>Numeric#divmod</code>. + */ + +static VALUE +num_modulo(VALUE x, VALUE y) +{ + return rb_funcall(x, '-', 1, + rb_funcall(y, '*', 1, + rb_funcall(x, rb_intern("div"), 1, y))); +} /* * call-seq: - * num.divmod( aNumeric ) -> anArray - * + * num.remainder(numeric) -> real + * + * x.remainder(y) means x-y*(x/y).truncate + * + * See <code>Numeric#divmod</code>. + */ + +static VALUE +num_remainder(VALUE x, VALUE y) +{ + VALUE z = rb_funcall(x, '%', 1, y); + + if ((!rb_equal(z, INT2FIX(0))) && + ((RTEST(rb_funcall(x, '<', 1, INT2FIX(0))) && + RTEST(rb_funcall(y, '>', 1, INT2FIX(0)))) || + (RTEST(rb_funcall(x, '>', 1, INT2FIX(0))) && + RTEST(rb_funcall(y, '<', 1, INT2FIX(0)))))) { + return rb_funcall(z, '-', 1, y); + } + return z; +} + +/* + * call-seq: + * num.divmod(numeric) -> array + * * Returns an array containing the quotient and modulus obtained by - * dividing <i>num</i> by <i>aNumeric</i>. If <code>q, r = + * dividing <i>num</i> by <i>numeric</i>. If <code>q, r = * x.divmod(y)</code>, then * - * q = floor(float(x)/float(y)) - * x = q*y + r - * + * q = floor(x/y) + * x = q*y+r + * * The quotient is rounded toward -infinity, as shown in the following table: - * + * * a | b | a.divmod(b) | a/b | a.modulo(b) | a.remainder(b) * ------+-----+---------------+---------+-------------+--------------- * 13 | 4 | 3, 1 | 3 | 1 | 1 * ------+-----+---------------+---------+-------------+--------------- - * 13 | -4 | -4, -3 | -3 | -3 | 1 + * 13 | -4 | -4, -3 | -4 | -3 | 1 * ------+-----+---------------+---------+-------------+--------------- * -13 | 4 | -4, 3 | -4 | 3 | -1 * ------+-----+---------------+---------+-------------+--------------- @@ -305,10 +409,11 @@ num_div(x, y) * ------+-----+---------------+---------+-------------+--------------- * -11.5 | 4 | -3, 0.5 | -2.875 | 0.5 | -3.5 * ------+-----+---------------+---------+-------------+--------------- - * -11.5 | -4 | 2 -3.5 | 2.875 | -3.5 | -3.5 + * -11.5 | -4 | 2, -3.5 | 2.875 | -3.5 | -3.5 * * * Examples + * * 11.divmod(3) #=> [3, 2] * 11.divmod(-3) #=> [-4, -1] * 11.divmod(3.5) #=> [3, 0.5] @@ -317,84 +422,53 @@ num_div(x, y) */ static VALUE -num_divmod(x, y) - VALUE x, y; +num_divmod(VALUE x, VALUE y) { - return rb_assoc_new(num_div(x, y), rb_funcall(x, '%', 1, y)); + return rb_assoc_new(num_div(x, y), num_modulo(x, y)); } /* * call-seq: - * num.modulo(numeric) => result - * - * Equivalent to - * <i>num</i>.<code>divmod(</code><i>aNumeric</i><code>)[1]</code>. - */ - -static VALUE -num_modulo(x, y) - VALUE x, y; -{ - return rb_funcall(x, '%', 1, y); -} - -/* - * call-seq: - * num.remainder(numeric) => result - * - * If <i>num</i> and <i>numeric</i> have different signs, returns - * <em>mod</em>-<i>numeric</i>; otherwise, returns <em>mod</em>. In - * both cases <em>mod</em> is the value - * <i>num</i>.<code>modulo(</code><i>numeric</i><code>)</code>. The - * differences between <code>remainder</code> and modulo - * (<code>%</code>) are shown in the table under <code>Numeric#divmod</code>. + * num.real? -> true or false + * + * Returns <code>true</code> if <i>num</i> is a <code>Real</code> + * (i.e. non <code>Complex</code>). */ static VALUE -num_remainder(x, y) - VALUE x, y; +num_real_p(VALUE num) { - VALUE z = rb_funcall(x, '%', 1, y); - - if ((!rb_equal(z, INT2FIX(0))) && - ((RTEST(rb_funcall(x, '<', 1, INT2FIX(0))) && - RTEST(rb_funcall(y, '>', 1, INT2FIX(0)))) || - (RTEST(rb_funcall(x, '>', 1, INT2FIX(0))) && - RTEST(rb_funcall(y, '<', 1, INT2FIX(0)))))) { - return rb_funcall(z, '-', 1, y); - } - return z; + return Qtrue; } /* * call-seq: - * num.integer? -> true or false - * + * num.integer? -> true or false + * * Returns <code>true</code> if <i>num</i> is an <code>Integer</code> * (including <code>Fixnum</code> and <code>Bignum</code>). */ static VALUE -num_int_p(num) - VALUE num; +num_int_p(VALUE num) { return Qfalse; } /* * call-seq: - * num.abs => num or numeric - * + * num.abs -> numeric + * num.magnitude -> numeric + * * Returns the absolute value of <i>num</i>. - * + * * 12.abs #=> 12 * (-34.56).abs #=> 34.56 * -34.56.abs #=> 34.56 */ static VALUE -num_abs(num) - VALUE num; +num_abs(VALUE num) { if (RTEST(rb_funcall(num, '<', 1, INT2FIX(0)))) { return rb_funcall(num, rb_intern("-@"), 0); @@ -405,14 +479,13 @@ num_abs(num) /* * call-seq: - * num.zero? => true or false - * + * num.zero? -> true or false + * * Returns <code>true</code> if <i>num</i> has a zero value. */ static VALUE -num_zero_p(num) - VALUE num; +num_zero_p(VALUE num) { if (rb_equal(num, INT2FIX(0))) { return Qtrue; @@ -423,19 +496,18 @@ num_zero_p(num) /* * call-seq: - * num.nonzero? => num or nil - * - * Returns <i>num</i> if <i>num</i> is not zero, <code>nil</code> + * num.nonzero? -> self or nil + * + * Returns +self+ if <i>num</i> is not zero, <code>nil</code> * otherwise. This behavior is useful when chaining comparisons: - * + * * a = %w( z Bb bB bb BB a aA Aa AA A ) * b = a.sort {|a,b| (a.downcase <=> b.downcase).nonzero? || a <=> b } * b #=> ["A", "a", "AA", "Aa", "aA", "BB", "Bb", "bB", "bb", "z"] */ static VALUE -num_nonzero_p(num) - VALUE num; +num_nonzero_p(VALUE num) { if (RTEST(rb_funcall(num, rb_intern("zero?"), 0, 0))) { return Qnil; @@ -445,43 +517,42 @@ num_nonzero_p(num) /* * call-seq: - * num.to_int => integer - * + * num.to_int -> integer + * * Invokes the child class's <code>to_i</code> method to convert * <i>num</i> to an integer. */ static VALUE -num_to_int(num) - VALUE num; +num_to_int(VALUE num) { return rb_funcall(num, id_to_i, 0, 0); } /******************************************************************** - * + * * Document-class: Float * - * <code>Float</code> objects represent real numbers using the native - * architecture's double-precision floating point representation. + * <code>Float</code> objects represent inexact real numbers using + * the native architecture's double-precision floating point + * representation. */ VALUE -rb_float_new(d) - double d; +rb_float_new(double d) { NEWOBJ(flt, struct RFloat); OBJSETUP(flt, rb_cFloat, T_FLOAT); - flt->value = d; + flt->float_value = d; return (VALUE)flt; } /* * call-seq: - * flt.to_s => string - * + * flt.to_s -> string + * * Returns a string containing a representation of self. As well as a * fixed or exponential form of the number, the call may return * ``<code>NaN</code>'', ``<code>Infinity</code>'', and @@ -489,33 +560,72 @@ rb_float_new(d) */ static VALUE -flo_to_s(flt) - VALUE flt; +flo_to_s(VALUE flt) { - char buf[32]; - double value = RFLOAT(flt)->value; + char *ruby_dtoa(double d_, int mode, int ndigits, int *decpt, int *sign, char **rve); + enum {decimal_mant = DBL_MANT_DIG-DBL_DIG}; + enum {float_dig = DBL_DIG+1}; + char buf[float_dig + (decimal_mant + CHAR_BIT - 1) / CHAR_BIT + 10]; + double value = RFLOAT_VALUE(flt); + VALUE s; char *p, *e; + int sign, decpt, digs; if (isinf(value)) - return rb_str_new2(value < 0 ? "-Infinity" : "Infinity"); - else if(isnan(value)) - return rb_str_new2("NaN"); - - sprintf(buf, "%#.15g", value); /* ensure to print decimal point */ - if (!(e = strchr(buf, 'e'))) { - e = buf + strlen(buf); - } - if (!ISDIGIT(e[-1])) { /* reformat if ended with decimal point (ex 111111111111111.) */ - sprintf(buf, "%#.14e", value); - if (!(e = strchr(buf, 'e'))) { - e = buf + strlen(buf); + return rb_usascii_str_new2(value < 0 ? "-Infinity" : "Infinity"); + else if (isnan(value)) + return rb_usascii_str_new2("NaN"); + + p = ruby_dtoa(value, 0, 0, &decpt, &sign, &e); + s = sign ? rb_usascii_str_new_cstr("-") : rb_usascii_str_new(0, 0); + if ((digs = (int)(e - p)) >= (int)sizeof(buf)) digs = (int)sizeof(buf) - 1; + memcpy(buf, p, digs); + xfree(p); + if (decpt > 0) { + if (decpt < digs) { + memmove(buf + decpt + 1, buf + decpt, digs - decpt); + buf[decpt] = '.'; + rb_str_cat(s, buf, digs + 1); + } + else if (decpt - digs < float_dig) { + long len; + char *ptr; + rb_str_cat(s, buf, digs); + rb_str_resize(s, (len = RSTRING_LEN(s)) + decpt - digs + 2); + ptr = RSTRING_PTR(s) + len; + if (decpt > digs) { + memset(ptr, '0', decpt - digs); + ptr += decpt - digs; + } + memcpy(ptr, ".0", 2); } + else { + goto exp; + } + } + else if (decpt > -4) { + long len; + char *ptr; + rb_str_cat(s, "0.", 2); + rb_str_resize(s, (len = RSTRING_LEN(s)) - decpt + digs); + ptr = RSTRING_PTR(s); + memset(ptr += len, '0', -decpt); + memcpy(ptr -= decpt, buf, digs); } - p = e; - while (p[-1]=='0' && ISDIGIT(p[-2])) - p--; - memmove(p, e, strlen(e)+1); - return rb_str_new2(buf); + else { + exp: + if (digs > 1) { + memmove(buf + 2, buf + 1, digs - 1); + } + else { + buf[2] = '0'; + digs++; + } + buf[1] = '.'; + rb_str_cat(s, buf, digs + 1); + rb_str_catf(s, "e%+03d", decpt - 1); + } + return s; } /* @@ -523,109 +633,103 @@ flo_to_s(flt) */ static VALUE -flo_coerce(x, y) - VALUE x, y; +flo_coerce(VALUE x, VALUE y) { return rb_assoc_new(rb_Float(y), x); } /* * call-seq: - * -float => float + * -float -> float * * Returns float, negated. */ static VALUE -flo_uminus(flt) - VALUE flt; +flo_uminus(VALUE flt) { - return rb_float_new(-RFLOAT(flt)->value); + return DBL2NUM(-RFLOAT_VALUE(flt)); } /* * call-seq: - * float + other => float + * float + other -> float * * Returns a new float which is the sum of <code>float</code> * and <code>other</code>. */ static VALUE -flo_plus(x, y) - VALUE x, y; +flo_plus(VALUE x, VALUE y) { switch (TYPE(y)) { case T_FIXNUM: - return rb_float_new(RFLOAT(x)->value + (double)FIX2LONG(y)); + return DBL2NUM(RFLOAT_VALUE(x) + (double)FIX2LONG(y)); case T_BIGNUM: - return rb_float_new(RFLOAT(x)->value + rb_big2dbl(y)); + return DBL2NUM(RFLOAT_VALUE(x) + rb_big2dbl(y)); case T_FLOAT: - return rb_float_new(RFLOAT(x)->value + RFLOAT(y)->value); + return DBL2NUM(RFLOAT_VALUE(x) + RFLOAT_VALUE(y)); default: - return rb_num_coerce_bin(x, y); + return rb_num_coerce_bin(x, y, '+'); } } /* * call-seq: - * float + other => float + * float - other -> float * * Returns a new float which is the difference of <code>float</code> * and <code>other</code>. */ static VALUE -flo_minus(x, y) - VALUE x, y; +flo_minus(VALUE x, VALUE y) { switch (TYPE(y)) { case T_FIXNUM: - return rb_float_new(RFLOAT(x)->value - (double)FIX2LONG(y)); + return DBL2NUM(RFLOAT_VALUE(x) - (double)FIX2LONG(y)); case T_BIGNUM: - return rb_float_new(RFLOAT(x)->value - rb_big2dbl(y)); + return DBL2NUM(RFLOAT_VALUE(x) - rb_big2dbl(y)); case T_FLOAT: - return rb_float_new(RFLOAT(x)->value - RFLOAT(y)->value); + return DBL2NUM(RFLOAT_VALUE(x) - RFLOAT_VALUE(y)); default: - return rb_num_coerce_bin(x, y); + return rb_num_coerce_bin(x, y, '-'); } } /* * call-seq: - * float * other => float + * float * other -> float * * Returns a new float which is the product of <code>float</code> * and <code>other</code>. */ static VALUE -flo_mul(x, y) - VALUE x, y; +flo_mul(VALUE x, VALUE y) { switch (TYPE(y)) { case T_FIXNUM: - return rb_float_new(RFLOAT(x)->value * (double)FIX2LONG(y)); + return DBL2NUM(RFLOAT_VALUE(x) * (double)FIX2LONG(y)); case T_BIGNUM: - return rb_float_new(RFLOAT(x)->value * rb_big2dbl(y)); + return DBL2NUM(RFLOAT_VALUE(x) * rb_big2dbl(y)); case T_FLOAT: - return rb_float_new(RFLOAT(x)->value * RFLOAT(y)->value); + return DBL2NUM(RFLOAT_VALUE(x) * RFLOAT_VALUE(y)); default: - return rb_num_coerce_bin(x, y); + return rb_num_coerce_bin(x, y, '*'); } } /* * call-seq: - * float / other => float + * float / other -> float * * Returns a new float which is the result of dividing * <code>float</code> by <code>other</code>. */ static VALUE -flo_div(x, y) - VALUE x, y; +flo_div(VALUE x, VALUE y) { long f_y; double d; @@ -633,25 +737,36 @@ flo_div(x, y) switch (TYPE(y)) { case T_FIXNUM: f_y = FIX2LONG(y); - return rb_float_new(RFLOAT(x)->value / (double)f_y); + return DBL2NUM(RFLOAT_VALUE(x) / (double)f_y); case T_BIGNUM: d = rb_big2dbl(y); - return rb_float_new(RFLOAT(x)->value / d); + return DBL2NUM(RFLOAT_VALUE(x) / d); case T_FLOAT: - return rb_float_new(RFLOAT(x)->value / RFLOAT(y)->value); + return DBL2NUM(RFLOAT_VALUE(x) / RFLOAT_VALUE(y)); default: - return rb_num_coerce_bin(x, y); + return rb_num_coerce_bin(x, y, '/'); } } +/* + * call-seq: + * float.quo(numeric) -> float + * + * Returns float / numeric. + */ + +static VALUE +flo_quo(VALUE x, VALUE y) +{ + return rb_funcall(x, '/', 1, y); +} static void -flodivmod(x, y, divp, modp) - double x, y; - double *divp, *modp; +flodivmod(double x, double y, double *divp, double *modp) { double div, mod; + if (y == 0.0) rb_num_zerodiv(); #ifdef HAVE_FMOD mod = fmod(x, y); #else @@ -662,7 +777,10 @@ flodivmod(x, y, divp, modp) mod = x - z * y; } #endif - div = (x - mod) / y; + if (isinf(x) && !isinf(y) && !isnan(y)) + div = x; + else + div = (x - mod) / y; if (y*mod < 0) { mod += y; div -= 1.0; @@ -674,18 +792,17 @@ flodivmod(x, y, divp, modp) /* * call-seq: - * flt % other => float - * flt.modulo(other) => float - * + * flt % other -> float + * flt.modulo(other) -> float + * * Return the modulo after division of <code>flt</code> by <code>other</code>. - * + * * 6543.21.modulo(137) #=> 104.21 * 6543.21.modulo(137.24) #=> 92.9299999999996 */ static VALUE -flo_mod(x, y) - VALUE x, y; +flo_mod(VALUE x, VALUE y) { double fy, mod; @@ -697,27 +814,36 @@ flo_mod(x, y) fy = rb_big2dbl(y); break; case T_FLOAT: - fy = RFLOAT(y)->value; + fy = RFLOAT_VALUE(y); break; default: - return rb_num_coerce_bin(x, y); + return rb_num_coerce_bin(x, y, '%'); } - flodivmod(RFLOAT(x)->value, fy, 0, &mod); - return rb_float_new(mod); + flodivmod(RFLOAT_VALUE(x), fy, 0, &mod); + return DBL2NUM(mod); +} + +static VALUE +dbl2ival(double d) +{ + if (FIXABLE(d)) { + d = round(d); + return LONG2FIX((long)d); + } + return rb_dbl2big(d); } /* * call-seq: - * flt.divmod(numeric) => array - * + * flt.divmod(numeric) -> array + * * See <code>Numeric#divmod</code>. */ static VALUE -flo_divmod(x, y) - VALUE x, y; +flo_divmod(VALUE x, VALUE y) { - double fy, div, mod, val; + double fy, div, mod; volatile VALUE a, b; switch (TYPE(y)) { @@ -728,62 +854,62 @@ flo_divmod(x, y) fy = rb_big2dbl(y); break; case T_FLOAT: - fy = RFLOAT(y)->value; + fy = RFLOAT_VALUE(y); break; default: - return rb_num_coerce_bin(x, y); + return rb_num_coerce_bin(x, y, rb_intern("divmod")); } - flodivmod(RFLOAT(x)->value, fy, &div, &mod); - if (FIXABLE(div)) { - val = div; - a = LONG2FIX(val); - } - else { - a = rb_dbl2big(div); - } - b = rb_float_new(mod); + flodivmod(RFLOAT_VALUE(x), fy, &div, &mod); + a = dbl2ival(div); + b = DBL2NUM(mod); return rb_assoc_new(a, b); } /* * call-seq: * - * flt ** other => float + * flt ** other -> float * * Raises <code>float</code> the <code>other</code> power. + * + * 2.0**3 #=> 8.0 */ - + static VALUE -flo_pow(x, y) - VALUE x, y; +flo_pow(VALUE x, VALUE y) { switch (TYPE(y)) { case T_FIXNUM: - return rb_float_new(pow(RFLOAT(x)->value, (double)FIX2LONG(y))); + return DBL2NUM(pow(RFLOAT_VALUE(x), (double)FIX2LONG(y))); case T_BIGNUM: - return rb_float_new(pow(RFLOAT(x)->value, rb_big2dbl(y))); + return DBL2NUM(pow(RFLOAT_VALUE(x), rb_big2dbl(y))); case T_FLOAT: - return rb_float_new(pow(RFLOAT(x)->value, RFLOAT(y)->value)); + { + double dx = RFLOAT_VALUE(x); + double dy = RFLOAT_VALUE(y); + if (dx < 0 && dy != round(dy)) + return rb_funcall(rb_complex_raw1(x), rb_intern("**"), 1, y); + return DBL2NUM(pow(dx, dy)); + } default: - return rb_num_coerce_bin(x, y); + return rb_num_coerce_bin(x, y, rb_intern("**")); } } /* * call-seq: - * num.eql?(numeric) => true or false - * + * num.eql?(numeric) -> true or false + * * Returns <code>true</code> if <i>num</i> and <i>numeric</i> are the * same type and have equal values. - * + * * 1 == 1.0 #=> true * 1.eql?(1.0) #=> false * (1.0).eql?(1.0) #=> true */ static VALUE -num_eql(x, y) - VALUE x, y; +num_eql(VALUE x, VALUE y) { if (TYPE(x) != TYPE(y)) return Qfalse; @@ -792,23 +918,21 @@ num_eql(x, y) /* * call-seq: - * num <=> other -> 0 or nil - * + * num <=> other -> 0 or nil + * * Returns zero if <i>num</i> equals <i>other</i>, <code>nil</code> * otherwise. */ static VALUE -num_cmp(x, y) - VALUE x, y; +num_cmp(VALUE x, VALUE y) { if (x == y) return INT2FIX(0); return Qnil; } static VALUE -num_equal(x, y) - VALUE x, y; +num_equal(VALUE x, VALUE y) { if (x == y) return Qtrue; return rb_funcall(y, id_eq, 1, x); @@ -816,69 +940,66 @@ num_equal(x, y) /* * call-seq: - * flt == obj => true or false - * + * flt == obj -> true or false + * * Returns <code>true</code> only if <i>obj</i> has the same value * as <i>flt</i>. Contrast this with <code>Float#eql?</code>, which * requires <i>obj</i> to be a <code>Float</code>. - * + * * 1.0 == 1 #=> true - * + * */ static VALUE -flo_eq(x, y) - VALUE x, y; +flo_eq(VALUE x, VALUE y) { volatile double a, b; switch (TYPE(y)) { case T_FIXNUM: - b = FIX2LONG(y); + b = (double)FIX2LONG(y); break; case T_BIGNUM: b = rb_big2dbl(y); break; case T_FLOAT: - b = RFLOAT(y)->value; + b = RFLOAT_VALUE(y); +#if defined(_MSC_VER) && _MSC_VER < 1300 if (isnan(b)) return Qfalse; +#endif break; default: return num_equal(x, y); } - a = RFLOAT(x)->value; + a = RFLOAT_VALUE(x); +#if defined(_MSC_VER) && _MSC_VER < 1300 if (isnan(a)) return Qfalse; +#endif return (a == b)?Qtrue:Qfalse; } /* * call-seq: - * flt.hash => integer + * flt.hash -> integer * * Returns a hash code for this float. */ static VALUE -flo_hash(num) - VALUE num; +flo_hash(VALUE num) { double d; - char *c; - int i, hash; + st_index_t hash; - d = RFLOAT(num)->value; - if (d == 0) d = fabs(d); - c = (char*)&d; - for (hash=0, i=0; i<sizeof(double);i++) { - hash += c[i] * 971; - } - if (hash < 0) hash = -hash; - return INT2FIX(hash); + d = RFLOAT_VALUE(num); + /* normalize -0.0 to 0.0 */ + if (d == 0.0) d = 0.0; + hash = rb_memhash(&d, sizeof(d)); + return LONG2FIX(hash); } VALUE -rb_dbl_cmp(a, b) - double a, b; +rb_dbl_cmp(double a, double b) { if (isnan(a) || isnan(b)) return Qnil; if (a == b) return INT2FIX(0); @@ -889,53 +1010,61 @@ rb_dbl_cmp(a, b) /* * call-seq: - * flt <=> numeric => -1, 0, +1 - * - * Returns -1, 0, or +1 depending on whether <i>flt</i> is less than, - * equal to, or greater than <i>numeric</i>. This is the basis for the - * tests in <code>Comparable</code>. + * flt <=> real -> -1, 0, +1 or nil + * + * Returns -1, 0, +1 or nil depending on whether <i>flt</i> is less + * than, equal to, or greater than <i>real</i>. This is the basis for + * the tests in <code>Comparable</code>. */ static VALUE -flo_cmp(x, y) - VALUE x, y; +flo_cmp(VALUE x, VALUE y) { double a, b; - a = RFLOAT(x)->value; + a = RFLOAT_VALUE(x); + if (isnan(a)) return Qnil; switch (TYPE(y)) { case T_FIXNUM: b = (double)FIX2LONG(y); break; case T_BIGNUM: + if (isinf(a)) { + if (a > 0.0) return INT2FIX(1); + else return INT2FIX(-1); + } b = rb_big2dbl(y); break; case T_FLOAT: - b = RFLOAT(y)->value; + b = RFLOAT_VALUE(y); break; default: - return rb_num_coerce_cmp(x, y); + if (isinf(a) && (!rb_respond_to(y, rb_intern("infinite?")) || + !RTEST(rb_funcall(y, rb_intern("infinite?"), 0, 0)))) { + if (a > 0.0) return INT2FIX(1); + return INT2FIX(-1); + } + return rb_num_coerce_cmp(x, y, rb_intern("<=>")); } return rb_dbl_cmp(a, b); } /* * call-seq: - * flt > other => true or false + * flt > real -> true or false * - * <code>true</code> if <code>flt</code> is greater than <code>other</code>. + * <code>true</code> if <code>flt</code> is greater than <code>real</code>. */ static VALUE -flo_gt(x, y) - VALUE x, y; +flo_gt(VALUE x, VALUE y) { double a, b; - a = RFLOAT(x)->value; + a = RFLOAT_VALUE(x); switch (TYPE(y)) { case T_FIXNUM: b = (double)FIX2LONG(y); @@ -946,32 +1075,35 @@ flo_gt(x, y) break; case T_FLOAT: - b = RFLOAT(y)->value; + b = RFLOAT_VALUE(y); +#if defined(_MSC_VER) && _MSC_VER < 1300 if (isnan(b)) return Qfalse; +#endif break; default: - return rb_num_coerce_relop(x, y); + return rb_num_coerce_relop(x, y, '>'); } +#if defined(_MSC_VER) && _MSC_VER < 1300 if (isnan(a)) return Qfalse; +#endif return (a > b)?Qtrue:Qfalse; } /* * call-seq: - * flt >= other => true or false + * flt >= real -> true or false * - * <code>true</code> if <code>flt</code> is greater than - * or equal to <code>other</code>. + * <code>true</code> if <code>flt</code> is greater than + * or equal to <code>real</code>. */ static VALUE -flo_ge(x, y) - VALUE x, y; +flo_ge(VALUE x, VALUE y) { double a, b; - a = RFLOAT(x)->value; + a = RFLOAT_VALUE(x); switch (TYPE(y)) { case T_FIXNUM: b = (double)FIX2LONG(y); @@ -982,31 +1114,34 @@ flo_ge(x, y) break; case T_FLOAT: - b = RFLOAT(y)->value; + b = RFLOAT_VALUE(y); +#if defined(_MSC_VER) && _MSC_VER < 1300 if (isnan(b)) return Qfalse; +#endif break; default: - return rb_num_coerce_relop(x, y); + return rb_num_coerce_relop(x, y, rb_intern(">=")); } +#if defined(_MSC_VER) && _MSC_VER < 1300 if (isnan(a)) return Qfalse; +#endif return (a >= b)?Qtrue:Qfalse; } /* * call-seq: - * flt < other => true or false + * flt < real -> true or false * - * <code>true</code> if <code>flt</code> is less than <code>other</code>. + * <code>true</code> if <code>flt</code> is less than <code>real</code>. */ static VALUE -flo_lt(x, y) - VALUE x, y; +flo_lt(VALUE x, VALUE y) { double a, b; - a = RFLOAT(x)->value; + a = RFLOAT_VALUE(x); switch (TYPE(y)) { case T_FIXNUM: b = (double)FIX2LONG(y); @@ -1017,32 +1152,35 @@ flo_lt(x, y) break; case T_FLOAT: - b = RFLOAT(y)->value; + b = RFLOAT_VALUE(y); +#if defined(_MSC_VER) && _MSC_VER < 1300 if (isnan(b)) return Qfalse; +#endif break; default: - return rb_num_coerce_relop(x, y); + return rb_num_coerce_relop(x, y, '<'); } +#if defined(_MSC_VER) && _MSC_VER < 1300 if (isnan(a)) return Qfalse; +#endif return (a < b)?Qtrue:Qfalse; } /* * call-seq: - * flt <= other => true or false + * flt <= real -> true or false * * <code>true</code> if <code>flt</code> is less than - * or equal to <code>other</code>. + * or equal to <code>real</code>. */ static VALUE -flo_le(x, y) - VALUE x, y; +flo_le(VALUE x, VALUE y) { double a, b; - a = RFLOAT(x)->value; + a = RFLOAT_VALUE(x); switch (TYPE(y)) { case T_FIXNUM: b = (double)FIX2LONG(y); @@ -1053,88 +1191,91 @@ flo_le(x, y) break; case T_FLOAT: - b = RFLOAT(y)->value; + b = RFLOAT_VALUE(y); +#if defined(_MSC_VER) && _MSC_VER < 1300 if (isnan(b)) return Qfalse; +#endif break; default: - return rb_num_coerce_relop(x, y); + return rb_num_coerce_relop(x, y, rb_intern("<=")); } +#if defined(_MSC_VER) && _MSC_VER < 1300 if (isnan(a)) return Qfalse; +#endif return (a <= b)?Qtrue:Qfalse; } /* * call-seq: - * flt.eql?(obj) => true or false - * + * flt.eql?(obj) -> true or false + * * Returns <code>true</code> only if <i>obj</i> is a * <code>Float</code> with the same value as <i>flt</i>. Contrast this * with <code>Float#==</code>, which performs type conversions. - * + * * 1.0.eql?(1) #=> false */ static VALUE -flo_eql(x, y) - VALUE x, y; +flo_eql(VALUE x, VALUE y) { if (TYPE(y) == T_FLOAT) { - double a = RFLOAT(x)->value; - double b = RFLOAT(y)->value; - + double a = RFLOAT_VALUE(x); + double b = RFLOAT_VALUE(y); +#if defined(_MSC_VER) && _MSC_VER < 1300 if (isnan(a) || isnan(b)) return Qfalse; - if (a == b) return Qtrue; +#endif + if (a == b) + return Qtrue; } return Qfalse; } /* * call-seq: - * flt.to_f => flt + * flt.to_f -> self * - * As <code>flt</code> is already a float, returns <i>self</i>. + * As <code>flt</code> is already a float, returns +self+. */ static VALUE -flo_to_f(num) - VALUE num; +flo_to_f(VALUE num) { return num; } /* * call-seq: - * flt.abs => float - * + * flt.abs -> float + * flt.magnitude -> float + * * Returns the absolute value of <i>flt</i>. - * + * * (-34.56).abs #=> 34.56 * -34.56.abs #=> 34.56 - * + * */ static VALUE -flo_abs(flt) - VALUE flt; +flo_abs(VALUE flt) { - double val = fabs(RFLOAT(flt)->value); - return rb_float_new(val); + double val = fabs(RFLOAT_VALUE(flt)); + return DBL2NUM(val); } /* * call-seq: - * flt.zero? -> true or false - * + * flt.zero? -> true or false + * * Returns <code>true</code> if <i>flt</i> is 0.0. - * + * */ static VALUE -flo_zero_p(num) - VALUE num; +flo_zero_p(VALUE num) { - if (RFLOAT(num)->value == 0.0) { + if (RFLOAT_VALUE(num) == 0.0) { return Qtrue; } return Qfalse; @@ -1142,11 +1283,11 @@ flo_zero_p(num) /* * call-seq: - * flt.nan? -> true or false - * + * flt.nan? -> true or false + * * Returns <code>true</code> if <i>flt</i> is an invalid IEEE floating * point number. - * + * * a = -1.0 #=> -1.0 * a.nan? #=> false * a = 0.0/0.0 #=> NaN @@ -1154,31 +1295,29 @@ flo_zero_p(num) */ static VALUE -flo_is_nan_p(num) - VALUE num; -{ - double value = RFLOAT(num)->value; +flo_is_nan_p(VALUE num) +{ + double value = RFLOAT_VALUE(num); return isnan(value) ? Qtrue : Qfalse; } /* * call-seq: - * flt.infinite? -> nil, -1, +1 - * + * flt.infinite? -> nil, -1, +1 + * * Returns <code>nil</code>, -1, or +1 depending on whether <i>flt</i> * is finite, -infinity, or +infinity. - * + * * (0.0).infinite? #=> nil * (-1.0/0.0).infinite? #=> -1 * (+1.0/0.0).infinite? #=> 1 */ static VALUE -flo_is_infinite_p(num) - VALUE num; -{ - double value = RFLOAT(num)->value; +flo_is_infinite_p(VALUE num) +{ + double value = RFLOAT_VALUE(num); if (isinf(value)) { return INT2FIX( value < 0 ? -1 : 1 ); @@ -1189,19 +1328,18 @@ flo_is_infinite_p(num) /* * call-seq: - * flt.finite? -> true or false - * + * flt.finite? -> true or false + * * Returns <code>true</code> if <i>flt</i> is a valid IEEE floating * point number (it is not infinite, and <code>nan?</code> is * <code>false</code>). - * + * */ static VALUE -flo_is_finite_p(num) - VALUE num; -{ - double value = RFLOAT(num)->value; +flo_is_finite_p(VALUE num) +{ + double value = RFLOAT_VALUE(num); #if HAVE_FINITE if (!finite(value)) @@ -1216,10 +1354,10 @@ flo_is_finite_p(num) /* * call-seq: - * flt.floor => integer - * + * flt.floor -> integer + * * Returns the largest integer less than or equal to <i>flt</i>. - * + * * 1.2.floor #=> 1 * 2.0.floor #=> 2 * (-1.2).floor #=> -2 @@ -1227,26 +1365,25 @@ flo_is_finite_p(num) */ static VALUE -flo_floor(num) - VALUE num; +flo_floor(VALUE num) { - double f = floor(RFLOAT(num)->value); + double f = floor(RFLOAT_VALUE(num)); long val; if (!FIXABLE(f)) { return rb_dbl2big(f); } - val = f; + val = (long)f; return LONG2FIX(val); } /* * call-seq: - * flt.ceil => integer - * + * flt.ceil -> integer + * * Returns the smallest <code>Integer</code> greater than or equal to * <i>flt</i>. - * + * * 1.2.ceil #=> 2 * 2.0.ceil #=> 2 * (-1.2).ceil #=> -1 @@ -1254,67 +1391,112 @@ flo_floor(num) */ static VALUE -flo_ceil(num) - VALUE num; +flo_ceil(VALUE num) { - double f = ceil(RFLOAT(num)->value); + double f = ceil(RFLOAT_VALUE(num)); long val; if (!FIXABLE(f)) { return rb_dbl2big(f); } - val = f; + val = (long)f; return LONG2FIX(val); } /* * call-seq: - * flt.round => integer - * - * Rounds <i>flt</i> to the nearest integer. Equivalent to: - * - * def round - * return floor(self+0.5) if self > 0.0 - * return ceil(self-0.5) if self < 0.0 - * return 0.0 - * end - * + * flt.round([ndigits]) -> integer or float + * + * Rounds <i>flt</i> to a given precision in decimal digits (default 0 digits). + * Precision may be negative. Returns a floating point number when ndigits + * is more than zero. + * + * 1.4.round #=> 1 * 1.5.round #=> 2 + * 1.6.round #=> 2 * (-1.5).round #=> -2 - * + * + * 1.234567.round(2) #=> 1.23 + * 1.234567.round(3) #=> 1.235 + * 1.234567.round(4) #=> 1.2346 + * 1.234567.round(5) #=> 1.23457 + * + * 34567.89.round(-5) #=> 0 + * 34567.89.round(-4) #=> 30000 + * 34567.89.round(-3) #=> 35000 + * 34567.89.round(-2) #=> 34600 + * 34567.89.round(-1) #=> 34570 + * 34567.89.round(0) #=> 34568 + * 34567.89.round(1) #=> 34567.9 + * 34567.89.round(2) #=> 34567.89 + * 34567.89.round(3) #=> 34567.89 + * */ static VALUE -flo_round(num) - VALUE num; +flo_round(int argc, VALUE *argv, VALUE num) { - double f = RFLOAT(num)->value; + VALUE nd; + double number, f; + int ndigits = 0, i; long val; - if (f > 0.0) f = floor(f+0.5); - if (f < 0.0) f = ceil(f-0.5); + if (argc > 0 && rb_scan_args(argc, argv, "01", &nd) == 1) { + ndigits = NUM2INT(nd); + } + number = RFLOAT_VALUE(num); + f = 1.0; + i = abs(ndigits); + while (--i >= 0) + f = f*10.0; - if (!FIXABLE(f)) { - return rb_dbl2big(f); + if (isinf(f)) { + if (ndigits < 0) number = 0; } - val = f; + else { + if (ndigits < 0) { + double absnum = fabs(number); + if (absnum < f) return INT2FIX(0); + if (!FIXABLE(number)) { + VALUE f10 = int_pow(10, -ndigits); + VALUE n10 = f10; + if (number < 0) { + extern VALUE rb_big_uminus(VALUE x); + f10 = FIXNUM_P(f10) ? fix_uminus(f10) : rb_big_uminus(f10); + } + num = rb_big_idiv(rb_dbl2big(absnum), n10); + return FIXNUM_P(num) ? fix_mul(num, f10) : rb_big_mul(num, f10); + } + number /= f; + } + else number *= f; + number = round(number); + if (ndigits < 0) number *= f; + else number /= f; + } + + if (ndigits > 0) return DBL2NUM(number); + + if (!FIXABLE(number)) { + return rb_dbl2big(number); + } + val = (long)number; return LONG2FIX(val); } /* * call-seq: - * flt.to_i => integer - * flt.to_int => integer - * flt.truncate => integer - * + * flt.to_i -> integer + * flt.to_int -> integer + * flt.truncate -> integer + * * Returns <i>flt</i> truncated to an <code>Integer</code>. */ static VALUE -flo_truncate(num) - VALUE num; +flo_truncate(VALUE num) { - double f = RFLOAT(num)->value; + double f = RFLOAT_VALUE(num); long val; if (f > 0.0) f = floor(f); @@ -1323,26 +1505,24 @@ flo_truncate(num) if (!FIXABLE(f)) { return rb_dbl2big(f); } - val = f; + val = (long)f; return LONG2FIX(val); } - /* * call-seq: - * num.floor => integer - * + * num.floor -> integer + * * Returns the largest integer less than or equal to <i>num</i>. * <code>Numeric</code> implements this by converting <i>anInteger</i> * to a <code>Float</code> and invoking <code>Float#floor</code>. - * + * * 1.floor #=> 1 * (-1).floor #=> -1 */ static VALUE -num_floor(num) - VALUE num; +num_floor(VALUE num) { return flo_floor(rb_Float(num)); } @@ -1350,13 +1530,13 @@ num_floor(num) /* * call-seq: - * num.ceil => integer - * + * num.ceil -> integer + * * Returns the smallest <code>Integer</code> greater than or equal to * <i>num</i>. Class <code>Numeric</code> achieves this by converting * itself to a <code>Float</code> then invoking * <code>Float#ceil</code>. - * + * * 1.ceil #=> 1 * 1.2.ceil #=> 2 * (-1.2).ceil #=> -1 @@ -1364,80 +1544,107 @@ num_floor(num) */ static VALUE -num_ceil(num) - VALUE num; +num_ceil(VALUE num) { return flo_ceil(rb_Float(num)); } /* * call-seq: - * num.round => integer - * - * Rounds <i>num</i> to the nearest integer. <code>Numeric</code> - * implements this by converting itself to a - * <code>Float</code> and invoking <code>Float#round</code>. + * num.round([ndigits]) -> integer or float + * + * Rounds <i>num</i> to a given precision in decimal digits (default 0 digits). + * Precision may be negative. Returns a floating point number when ndigits + * is more than zero. <code>Numeric</code> implements this by converting itself + * to a <code>Float</code> and invoking <code>Float#round</code>. */ static VALUE -num_round(num) - VALUE num; +num_round(int argc, VALUE* argv, VALUE num) { - return flo_round(rb_Float(num)); + return flo_round(argc, argv, rb_Float(num)); } /* * call-seq: - * num.truncate => integer - * + * num.truncate -> integer + * * Returns <i>num</i> truncated to an integer. <code>Numeric</code> * implements this by converting its value to a float and invoking * <code>Float#truncate</code>. */ static VALUE -num_truncate(num) - VALUE num; +num_truncate(VALUE num) { return flo_truncate(rb_Float(num)); } +int +ruby_float_step(VALUE from, VALUE to, VALUE step, int excl) +{ + if (TYPE(from) == T_FLOAT || TYPE(to) == T_FLOAT || TYPE(step) == T_FLOAT) { + const double epsilon = DBL_EPSILON; + double beg = NUM2DBL(from); + double end = NUM2DBL(to); + double unit = NUM2DBL(step); + double n = (end - beg)/unit; + double err = (fabs(beg) + fabs(end) + fabs(end-beg)) / fabs(unit) * epsilon; + long i; + + if (isinf(unit)) { + if (unit > 0 ? beg <= end : beg >= end) rb_yield(DBL2NUM(beg)); + } + else { + if (err>0.5) err=0.5; + n = floor(n + err); + if (!excl || ((long)n)*unit+beg < end) n++; + for (i=0; i<n; i++) { + rb_yield(DBL2NUM(i*unit+beg)); + } + } + return TRUE; + } + return FALSE; +} + /* * call-seq: - * num.step(limit, step ) {|i| block } => num - * + * num.step(limit[, step]) {|i| block } -> self + * num.step(limit[, step]) -> an_enumerator + * * Invokes <em>block</em> with the sequence of numbers starting at - * <i>num</i>, incremented by <i>step</i> on each call. The loop - * finishes when the value to be passed to the block is greater than - * <i>limit</i> (if <i>step</i> is positive) or less than - * <i>limit</i> (if <i>step</i> is negative). If all the arguments are - * integers, the loop operates using an integer counter. If any of the - * arguments are floating point numbers, all are converted to floats, - * and the loop is executed <i>floor(n + n*epsilon)+ 1</i> times, - * where <i>n = (limit - num)/step</i>. Otherwise, the loop - * starts at <i>num</i>, uses either the <code><</code> or - * <code>></code> operator to compare the counter against - * <i>limit</i>, and increments itself using the <code>+</code> - * operator. - * + * <i>num</i>, incremented by <i>step</i> (default 1) on each + * call. The loop finishes when the value to be passed to the block + * is greater than <i>limit</i> (if <i>step</i> is positive) or less + * than <i>limit</i> (if <i>step</i> is negative). If all the + * arguments are integers, the loop operates using an integer + * counter. If any of the arguments are floating point numbers, all + * are converted to floats, and the loop is executed <i>floor(n + + * n*epsilon)+ 1</i> times, where <i>n = (limit - + * num)/step</i>. Otherwise, the loop starts at <i>num</i>, uses + * either the <code><</code> or <code>></code> operator to compare + * the counter against <i>limit</i>, and increments itself using the + * <code>+</code> operator. + * + * If no block is given, an enumerator is returned instead. + * * 1.step(10, 2) { |i| print i, " " } * Math::E.step(Math::PI, 0.2) { |f| print f, " " } - * + * * <em>produces:</em> - * + * * 1 3 5 7 9 * 2.71828182845905 2.91828182845905 3.11828182845905 */ static VALUE -num_step(argc, argv, from) - int argc; - VALUE *argv; - VALUE from; +num_step(int argc, VALUE *argv, VALUE from) { VALUE to, step; + RETURN_ENUMERATOR(from, argc, argv); if (argc == 1) { to = argv[0]; step = INT2FIX(1); @@ -1448,7 +1655,7 @@ num_step(argc, argv, from) step = argv[1]; } else { - rb_raise(rb_eArgError, "wrong number of arguments"); + rb_raise(rb_eArgError, "wrong number of arguments (%d for 1..2)", argc); } if (rb_equal(step, INT2FIX(0))) { rb_raise(rb_eArgError, "step can't be 0"); @@ -1475,22 +1682,7 @@ num_step(argc, argv, from) } } } - else if (TYPE(from) == T_FLOAT || TYPE(to) == T_FLOAT || TYPE(step) == T_FLOAT) { - const double epsilon = DBL_EPSILON; - double beg = NUM2DBL(from); - double end = NUM2DBL(to); - double unit = NUM2DBL(step); - double n = (end - beg)/unit; - double err = (fabs(beg) + fabs(end) + fabs(end-beg)) / fabs(unit) * epsilon; - long i; - - if (err>0.5) err=0.5; - n = floor(n + err) + 1; - for (i=0; i<n; i++) { - rb_yield(rb_float_new(i*unit+beg)); - } - } - else { + else if (!ruby_float_step(from, to, step, FALSE)) { VALUE i = from; ID cmp; @@ -1509,10 +1701,10 @@ num_step(argc, argv, from) return from; } -long -rb_num2long(val) - VALUE val; +SIGNED_VALUE +rb_num2long(VALUE val) { + again: if (NIL_P(val)) { rb_raise(rb_eTypeError, "no implicit conversion from nil to integer"); } @@ -1521,16 +1713,16 @@ rb_num2long(val) switch (TYPE(val)) { case T_FLOAT: - if (RFLOAT(val)->value <= (double)LONG_MAX - && RFLOAT(val)->value >= (double)LONG_MIN) { - return (long)(RFLOAT(val)->value); + if (RFLOAT_VALUE(val) <= (double)LONG_MAX + && RFLOAT_VALUE(val) >= (double)LONG_MIN) { + return (SIGNED_VALUE)(RFLOAT_VALUE(val)); } else { char buf[24]; char *s; - sprintf(buf, "%-.10g", RFLOAT(val)->value); - if (s = strchr(buf, ' ')) *s = '\0'; + snprintf(buf, sizeof(buf), "%-.10g", RFLOAT_VALUE(val)); + if ((s = strchr(buf, ' ')) != 0) *s = '\0'; rb_raise(rb_eRangeError, "float %s out of range of integer", buf); } @@ -1539,51 +1731,79 @@ rb_num2long(val) default: val = rb_to_int(val); - return NUM2LONG(val); + goto again; } } -unsigned long -rb_num2ulong(val) - VALUE val; +VALUE +rb_num2ulong(VALUE val) { - if (TYPE(val) == T_BIGNUM) { + again: + if (NIL_P(val)) { + rb_raise(rb_eTypeError, "no implicit conversion from nil to integer"); + } + + if (FIXNUM_P(val)) return FIX2LONG(val); /* this is FIX2LONG, inteneded */ + + switch (TYPE(val)) { + case T_FLOAT: + if (RFLOAT_VALUE(val) <= (double)LONG_MAX + && RFLOAT_VALUE(val) >= (double)LONG_MIN) { + return (VALUE)RFLOAT_VALUE(val); + } + else { + char buf[24]; + char *s; + + snprintf(buf, sizeof(buf), "%-.10g", RFLOAT_VALUE(val)); + if ((s = strchr(buf, ' ')) != 0) *s = '\0'; + rb_raise(rb_eRangeError, "float %s out of range of integer", buf); + } + + case T_BIGNUM: return rb_big2ulong(val); + + default: + val = rb_to_int(val); + goto again; } - return (unsigned long)rb_num2long(val); } -#if SIZEOF_INT < SIZEOF_LONG -static void -check_int(num) - long num; +#if SIZEOF_INT < SIZEOF_VALUE +void +rb_out_of_int(SIGNED_VALUE num) { - const char *s; + rb_raise(rb_eRangeError, "integer %"PRIdVALUE " too %s to convert to `int'", + num, num < 0 ? "small" : "big"); +} - if (num < INT_MIN) { - s = "small"; - } - else if (num > INT_MAX) { - s = "big"; - } - else { - return; +static void +check_int(SIGNED_VALUE num) +{ + if ((SIGNED_VALUE)(int)num != num) { + rb_out_of_int(num); } - rb_raise(rb_eRangeError, "integer %ld too %s to convert to `int'", num, s); } static void -check_uint(num) - unsigned long num; +check_uint(VALUE num, VALUE sign) { - if (num > UINT_MAX) { - rb_raise(rb_eRangeError, "integer %lu too big to convert to `unsigned int'", num); + static const VALUE mask = ~(VALUE)UINT_MAX; + + if (RTEST(sign)) { + /* minus */ + if ((num & mask) != mask || (num & ~mask) <= INT_MAX + 1UL) + rb_raise(rb_eRangeError, "integer %"PRIdVALUE " too small to convert to `unsigned int'", num); + } + else { + /* plus */ + if ((num & mask) != 0) + rb_raise(rb_eRangeError, "integer %"PRIuVALUE " too big to convert to `unsigned int'", num); } } long -rb_num2int(val) - VALUE val; +rb_num2int(VALUE val) { long num = rb_num2long(val); @@ -1592,8 +1812,7 @@ rb_num2int(val) } long -rb_fix2int(val) - VALUE val; +rb_fix2int(VALUE val) { long num = FIXNUM_P(val)?FIX2LONG(val):rb_num2long(val); @@ -1602,51 +1821,43 @@ rb_fix2int(val) } unsigned long -rb_num2uint(val) - VALUE val; +rb_num2uint(VALUE val) { unsigned long num = rb_num2ulong(val); - if (RTEST(rb_funcall(INT2FIX(0), '<', 1, val))) { - check_uint(num); - } + check_uint(num, rb_funcall(val, '<', 1, INT2FIX(0))); return num; } unsigned long -rb_fix2uint(val) - VALUE val; +rb_fix2uint(VALUE val) { unsigned long num; if (!FIXNUM_P(val)) { - return rb_num2uint(val); + return rb_num2uint(val); } num = FIX2ULONG(val); - if (FIX2LONG(val) > 0) { - check_uint(num); - } + + check_uint(num, rb_funcall(val, '<', 1, INT2FIX(0))); return num; } #else long -rb_num2int(val) - VALUE val; +rb_num2int(VALUE val) { return rb_num2long(val); } long -rb_fix2int(val) - VALUE val; +rb_fix2int(VALUE val) { return FIX2INT(val); } #endif VALUE -rb_num2fix(val) - VALUE val; +rb_num2fix(VALUE val) { long v; @@ -1654,15 +1865,14 @@ rb_num2fix(val) v = rb_num2long(val); if (!FIXABLE(v)) - rb_raise(rb_eRangeError, "integer %ld out of range of fixnum", v); + rb_raise(rb_eRangeError, "integer %"PRIdVALUE " out of range of fixnum", v); return LONG2FIX(v); } #if HAVE_LONG_LONG LONG_LONG -rb_num2ll(val) - VALUE val; +rb_num2ll(VALUE val) { if (NIL_P(val)) { rb_raise(rb_eTypeError, "no implicit conversion from nil"); @@ -1671,41 +1881,40 @@ rb_num2ll(val) if (FIXNUM_P(val)) return (LONG_LONG)FIX2LONG(val); switch (TYPE(val)) { - case T_FLOAT: - if (RFLOAT(val)->value <= (double)LLONG_MAX - && RFLOAT(val)->value >= (double)LLONG_MIN) { - return (LONG_LONG)(RFLOAT(val)->value); + case T_FLOAT: + if (RFLOAT_VALUE(val) <= (double)LLONG_MAX + && RFLOAT_VALUE(val) >= (double)LLONG_MIN) { + return (LONG_LONG)(RFLOAT_VALUE(val)); } else { char buf[24]; char *s; - sprintf(buf, "%-.10g", RFLOAT(val)->value); - if (s = strchr(buf, ' ')) *s = '\0'; + snprintf(buf, sizeof(buf), "%-.10g", RFLOAT_VALUE(val)); + if ((s = strchr(buf, ' ')) != 0) *s = '\0'; rb_raise(rb_eRangeError, "float %s out of range of long long", buf); } - case T_BIGNUM: + case T_BIGNUM: return rb_big2ll(val); - case T_STRING: + case T_STRING: rb_raise(rb_eTypeError, "no implicit conversion from string"); return Qnil; /* not reached */ - case T_TRUE: - case T_FALSE: + case T_TRUE: + case T_FALSE: rb_raise(rb_eTypeError, "no implicit conversion from boolean"); return Qnil; /* not reached */ default: - val = rb_to_int(val); - return NUM2LL(val); + val = rb_to_int(val); + return NUM2LL(val); } } unsigned LONG_LONG -rb_num2ull(val) - VALUE val; +rb_num2ull(VALUE val) { if (TYPE(val) == T_BIGNUM) { return rb_big2ull(val); @@ -1715,206 +1924,265 @@ rb_num2ull(val) #endif /* HAVE_LONG_LONG */ - /* * Document-class: Integer * * <code>Integer</code> is the basis for the two concrete classes that * hold whole numbers, <code>Bignum</code> and <code>Fixnum</code>. - * + * */ /* * call-seq: - * int.to_i => int - * int.to_int => int - * int.floor => int - * int.ceil => int - * int.round => int - * int.truncate => int + * int.to_i -> integer + * int.to_int -> integer + * int.floor -> integer + * int.ceil -> integer + * int.round -> integer + * int.truncate -> integer * * As <i>int</i> is already an <code>Integer</code>, all these * methods simply return the receiver. */ static VALUE -int_to_i(num) - VALUE num; +int_to_i(VALUE num) { return num; } /* * call-seq: - * int.integer? -> true - * + * int.integer? -> true + * * Always returns <code>true</code>. */ static VALUE -int_int_p(num) - VALUE num; +int_int_p(VALUE num) { return Qtrue; } /* * call-seq: - * int.next => integer - * int.succ => integer - * - * Returns the <code>Integer</code> equal to <i>int</i> + 1. - * - * 1.next #=> 2 - * (-1).next #=> 0 + * int.odd? -> true or false + * + * Returns <code>true</code> if <i>int</i> is an odd number. */ static VALUE -int_succ(num) - VALUE num; +int_odd_p(VALUE num) { - if (FIXNUM_P(num)) { - long i = FIX2LONG(num) + 1; - return LONG2NUM(i); + if (rb_funcall(num, '%', 1, INT2FIX(2)) != INT2FIX(0)) { + return Qtrue; } - return rb_funcall(num, '+', 1, INT2FIX(1)); + return Qfalse; } /* * call-seq: - * int.chr => string - * - * Returns a string containing the ASCII character represented by the - * receiver's value. - * - * 65.chr #=> "A" - * ?a.chr #=> "a" - * 230.chr #=> "\346" + * int.even? -> true or false + * + * Returns <code>true</code> if <i>int</i> is an even number. */ static VALUE -int_chr(num) - VALUE num; +int_even_p(VALUE num) { - char c; - long i = NUM2LONG(num); - - if (i < 0 || 0xff < i) - rb_raise(rb_eRangeError, "%ld out of char range", i); - c = i; - return rb_str_new(&c, 1); + if (rb_funcall(num, '%', 1, INT2FIX(2)) == INT2FIX(0)) { + return Qtrue; + } + return Qfalse; } -/******************************************************************** - * - * Document-class: Fixnum +/* + * call-seq: + * fixnum.next -> integer + * fixnum.succ -> integer * - * A <code>Fixnum</code> holds <code>Integer</code> values that can be - * represented in a native machine word (minus 1 bit). If any operation - * on a <code>Fixnum</code> exceeds this range, the value is - * automatically converted to a <code>Bignum</code>. - * - * <code>Fixnum</code> objects have immediate value. This means that - * when they are assigned or passed as parameters, the actual object is - * passed, rather than a reference to that object. Assignment does not - * alias <code>Fixnum</code> objects. There is effectively only one - * <code>Fixnum</code> object instance for any given integer value, so, - * for example, you cannot add a singleton method to a - * <code>Fixnum</code>. + * Returns the <code>Integer</code> equal to <i>int</i> + 1. + * + * 1.next #=> 2 + * (-1).next #=> 0 */ +static VALUE +fix_succ(VALUE num) +{ + long i = FIX2LONG(num) + 1; + return LONG2NUM(i); +} /* - * call-seq: - * Fixnum.induced_from(obj) => fixnum + * call-seq: + * int.next -> integer + * int.succ -> integer * - * Convert <code>obj</code> to a Fixnum. Works with numeric parameters. - * Also works with Symbols, but this is deprecated. + * Returns the <code>Integer</code> equal to <i>int</i> + 1. + * + * 1.next #=> 2 + * (-1).next #=> 0 */ static VALUE -rb_fix_induced_from(klass, x) - VALUE klass, x; +int_succ(VALUE num) { - return rb_num2fix(x); + if (FIXNUM_P(num)) { + long i = FIX2LONG(num) + 1; + return LONG2NUM(i); + } + return rb_funcall(num, '+', 1, INT2FIX(1)); } /* - * call-seq: - * Integer.induced_from(obj) => fixnum, bignum + * call-seq: + * int.pred -> integer * - * Convert <code>obj</code> to an Integer. + * Returns the <code>Integer</code> equal to <i>int</i> - 1. + * + * 1.pred #=> 0 + * (-1).pred #=> -2 */ static VALUE -rb_int_induced_from(klass, x) - VALUE klass, x; +int_pred(VALUE num) +{ + if (FIXNUM_P(num)) { + long i = FIX2LONG(num) - 1; + return LONG2NUM(i); + } + return rb_funcall(num, '-', 1, INT2FIX(1)); +} + +VALUE +rb_enc_uint_chr(unsigned int code, rb_encoding *enc) { - switch (TYPE(x)) { - case T_FIXNUM: - case T_BIGNUM: - return x; - case T_FLOAT: - return rb_funcall(x, id_to_i, 0); - default: - rb_raise(rb_eTypeError, "failed to convert %s into Integer", - rb_obj_classname(x)); + int n; + VALUE str; + if ((n = rb_enc_codelen(code, enc)) <= 0) { + rb_raise(rb_eRangeError, "%d out of char range", code); } + str = rb_enc_str_new(0, n, enc); + rb_enc_mbcput(code, RSTRING_PTR(str), enc); + return str; } /* - * call-seq: - * Float.induced_from(obj) => float + * call-seq: + * int.chr([encoding]) -> string + * + * Returns a string containing the character represented by the + * receiver's value according to +encoding+. * - * Convert <code>obj</code> to a float. + * 65.chr #=> "A" + * 230.chr #=> "\346" + * 255.chr(Encoding::UTF_8) #=> "\303\277" */ static VALUE -rb_flo_induced_from(klass, x) - VALUE klass, x; +int_chr(int argc, VALUE *argv, VALUE num) { - switch (TYPE(x)) { - case T_FIXNUM: - case T_BIGNUM: - return rb_funcall(x, rb_intern("to_f"), 0); - case T_FLOAT: - return x; - default: - rb_raise(rb_eTypeError, "failed to convert %s into Float", - rb_obj_classname(x)); + char c; + unsigned int i = NUM2UINT(num); + rb_encoding *enc; + + switch (argc) { + case 0: + if (i < 0) { + out_of_range: + rb_raise(rb_eRangeError, "%d out of char range", i); + } + if (0xff < i) { + enc = rb_default_internal_encoding(); + if (!enc) goto out_of_range; + goto decode; + } + c = (char)i; + if (i < 0x80) { + return rb_usascii_str_new(&c, 1); + } + else { + return rb_str_new(&c, 1); + } + case 1: + break; + default: + rb_raise(rb_eArgError, "wrong number of arguments (%d for 0..1)", argc); + break; } + enc = rb_to_encoding(argv[0]); + if (!enc) enc = rb_ascii8bit_encoding(); + decode: + return rb_enc_uint_chr(i, enc); } /* + * call-seq: + * int.ord -> self + * + * Returns the int itself. + * + * ?a.ord #=> 97 + * + * This method is intended for compatibility to + * character constant in Ruby 1.9. + * For example, ?a.ord returns 97 both in 1.8 and 1.9. + */ + +static VALUE +int_ord(num) + VALUE num; +{ + return num; +} + +/******************************************************************** + * + * Document-class: Fixnum + * + * A <code>Fixnum</code> holds <code>Integer</code> values that can be + * represented in a native machine word (minus 1 bit). If any operation + * on a <code>Fixnum</code> exceeds this range, the value is + * automatically converted to a <code>Bignum</code>. + * + * <code>Fixnum</code> objects have immediate value. This means that + * when they are assigned or passed as parameters, the actual object is + * passed, rather than a reference to that object. Assignment does not + * alias <code>Fixnum</code> objects. There is effectively only one + * <code>Fixnum</code> object instance for any given integer value, so, + * for example, you cannot add a singleton method to a + * <code>Fixnum</code>. + */ + + +/* * call-seq: - * -fix => integer + * -fix -> integer * * Negates <code>fix</code> (which might return a Bignum). */ static VALUE -fix_uminus(num) - VALUE num; +fix_uminus(VALUE num) { return LONG2NUM(-FIX2LONG(num)); } VALUE -rb_fix2str(x, base) - VALUE x; - int base; +rb_fix2str(VALUE x, int base) { extern const char ruby_digitmap[]; - char buf[SIZEOF_LONG*CHAR_BIT + 2], *b = buf + sizeof buf; + char buf[SIZEOF_VALUE*CHAR_BIT + 2], *b = buf + sizeof buf; long val = FIX2LONG(x); int neg = 0; if (base < 2 || 36 < base) { - rb_raise(rb_eArgError, "illegal radix %d", base); + rb_raise(rb_eArgError, "invalid radix %d", base); } if (val == 0) { - return rb_str_new2("0"); + return rb_usascii_str_new2("0"); } if (val < 0) { val = -val; @@ -1928,16 +2196,16 @@ rb_fix2str(x, base) *--b = '-'; } - return rb_str_new2(b); + return rb_usascii_str_new2(b); } /* * call-seq: - * fix.to_s( base=10 ) -> aString - * + * fix.to_s(base=10) -> string + * * Returns a string containing the representation of <i>fix</i> radix * <i>base</i> (between 2 and 36). - * + * * 12345.to_s #=> "12345" * 12345.to_s(2) #=> "11000000111001" * 12345.to_s(8) #=> "30071" @@ -1947,33 +2215,32 @@ rb_fix2str(x, base) * */ static VALUE -fix_to_s(argc, argv, x) - int argc; - VALUE *argv; - VALUE x; +fix_to_s(int argc, VALUE *argv, VALUE x) { - VALUE b; int base; - rb_scan_args(argc, argv, "01", &b); if (argc == 0) base = 10; - else base = NUM2INT(b); + else { + VALUE b; + + rb_scan_args(argc, argv, "01", &b); + base = NUM2INT(b); + } return rb_fix2str(x, base); } /* * call-seq: - * fix + numeric => numeric_result + * fix + numeric -> numeric_result * * Performs addition: the class of the resulting object depends on - * the class of <code>numeric</code> and on the magnitude of the + * the class of <code>numeric</code> and on the magnitude of the * result. */ static VALUE -fix_plus(x, y) - VALUE x, y; +fix_plus(VALUE x, VALUE y) { if (FIXNUM_P(y)) { long a, b, c; @@ -1982,31 +2249,31 @@ fix_plus(x, y) a = FIX2LONG(x); b = FIX2LONG(y); c = a + b; - r = LONG2FIX(c); + r = LONG2NUM(c); - if (FIX2LONG(r) != c) { - r = rb_big_plus(rb_int2big(a), rb_int2big(b)); - } return r; } - if (TYPE(y) == T_FLOAT) { - return rb_float_new((double)FIX2LONG(x) + RFLOAT(y)->value); + switch (TYPE(y)) { + case T_BIGNUM: + return rb_big_plus(y, x); + case T_FLOAT: + return DBL2NUM((double)FIX2LONG(x) + RFLOAT_VALUE(y)); + default: + return rb_num_coerce_bin(x, y, '+'); } - return rb_num_coerce_bin(x, y); } /* * call-seq: - * fix - numeric => numeric_result + * fix - numeric -> numeric_result * * Performs subtraction: the class of the resulting object depends on - * the class of <code>numeric</code> and on the magnitude of the + * the class of <code>numeric</code> and on the magnitude of the * result. */ static VALUE -fix_minus(x, y) - VALUE x, y; +fix_minus(VALUE x, VALUE y) { if (FIXNUM_P(y)) { long a, b, c; @@ -2015,58 +2282,82 @@ fix_minus(x, y) a = FIX2LONG(x); b = FIX2LONG(y); c = a - b; - r = LONG2FIX(c); + r = LONG2NUM(c); - if (FIX2LONG(r) != c) { - r = rb_big_minus(rb_int2big(a), rb_int2big(b)); - } return r; } - if (TYPE(y) == T_FLOAT) { - return rb_float_new((double)FIX2LONG(x) - RFLOAT(y)->value); + switch (TYPE(y)) { + case T_BIGNUM: + x = rb_int2big(FIX2LONG(x)); + return rb_big_minus(x, y); + case T_FLOAT: + return DBL2NUM((double)FIX2LONG(x) - RFLOAT_VALUE(y)); + default: + return rb_num_coerce_bin(x, y, '-'); } - return rb_num_coerce_bin(x, y); } +#define SQRT_LONG_MAX ((SIGNED_VALUE)1<<((SIZEOF_LONG*CHAR_BIT-1)/2)) +/*tests if N*N would overflow*/ +#define FIT_SQRT_LONG(n) (((n)<SQRT_LONG_MAX)&&((n)>=-SQRT_LONG_MAX)) + /* * call-seq: - * fix * numeric => numeric_result + * fix * numeric -> numeric_result * * Performs multiplication: the class of the resulting object depends on - * the class of <code>numeric</code> and on the magnitude of the + * the class of <code>numeric</code> and on the magnitude of the * result. */ static VALUE -fix_mul(x, y) - VALUE x, y; +fix_mul(VALUE x, VALUE y) { if (FIXNUM_P(y)) { - long a, b, c; +#ifdef __HP_cc +/* avoids an optimization bug of HP aC++/ANSI C B3910B A.06.05 [Jul 25 2005] */ + volatile +#endif + long a, b; +#if SIZEOF_LONG * 2 <= SIZEOF_LONG_LONG + LONG_LONG d; +#else + volatile long c; VALUE r; +#endif a = FIX2LONG(x); - if (a == 0) return x; - b = FIX2LONG(y); + +#if SIZEOF_LONG * 2 <= SIZEOF_LONG_LONG + d = (LONG_LONG)a * b; + if (FIXABLE(d)) return LONG2FIX(d); + return rb_ll2inum(d); +#else + if (FIT_SQRT_LONG(a) && FIT_SQRT_LONG(b)) + return LONG2FIX(a*b); c = a * b; r = LONG2FIX(c); + if (a == 0) return x; if (FIX2LONG(r) != c || c/a != b) { r = rb_big_mul(rb_int2big(a), rb_int2big(b)); } return r; +#endif } - if (TYPE(y) == T_FLOAT) { - return rb_float_new((double)FIX2LONG(x) * RFLOAT(y)->value); + switch (TYPE(y)) { + case T_BIGNUM: + return rb_big_mul(y, x); + case T_FLOAT: + return DBL2NUM((double)FIX2LONG(x) * RFLOAT_VALUE(y)); + default: + return rb_num_coerce_bin(x, y, '*'); } - return rb_num_coerce_bin(x, y); } static void -fixdivmod(x, y, divp, modp) - long x, y; - long *divp, *modp; +fixdivmod(long x, long y, long *divp, long *modp) { long div, mod; @@ -2092,63 +2383,113 @@ fixdivmod(x, y, divp, modp) if (modp) *modp = mod; } +VALUE rb_big_fdiv(VALUE x, VALUE y); + /* * call-seq: - * fix.quo(numeric) => float - * + * fix.fdiv(numeric) -> float + * * Returns the floating point result of dividing <i>fix</i> by * <i>numeric</i>. - * - * 654321.quo(13731) #=> 47.6528293642124 - * 654321.quo(13731.24) #=> 47.6519964693647 - * + * + * 654321.fdiv(13731) #=> 47.6528293642124 + * 654321.fdiv(13731.24) #=> 47.6519964693647 + * */ static VALUE -fix_quo(x, y) - VALUE x, y; +fix_fdiv(VALUE x, VALUE y) { if (FIXNUM_P(y)) { - return rb_float_new((double)FIX2LONG(x) / (double)FIX2LONG(y)); + return DBL2NUM((double)FIX2LONG(x) / (double)FIX2LONG(y)); + } + switch (TYPE(y)) { + case T_BIGNUM: + return rb_big_fdiv(rb_int2big(FIX2LONG(x)), y); + case T_FLOAT: + return DBL2NUM((double)FIX2LONG(x) / RFLOAT_VALUE(y)); + default: + return rb_num_coerce_bin(x, y, rb_intern("fdiv")); + } +} + +VALUE rb_rational_reciprocal(VALUE x); + +static VALUE +fix_divide(VALUE x, VALUE y, ID op) +{ + if (FIXNUM_P(y)) { + long div; + + fixdivmod(FIX2LONG(x), FIX2LONG(y), &div, 0); + return LONG2NUM(div); + } + switch (TYPE(y)) { + case T_BIGNUM: + x = rb_int2big(FIX2LONG(x)); + return rb_big_div(x, y); + case T_FLOAT: + { + double div; + + if (op == '/') { + div = (double)FIX2LONG(x) / RFLOAT_VALUE(y); + return DBL2NUM(div); + } + else { + if (RFLOAT_VALUE(y) == 0) rb_num_zerodiv(); + div = (double)FIX2LONG(x) / RFLOAT_VALUE(y); + return rb_dbl2big(floor(div)); + } + } + case T_RATIONAL: + if (op == '/' && FIX2LONG(x) == 1) + return rb_rational_reciprocal(y); + /* fall through */ + default: + return rb_num_coerce_bin(x, y, op); } - return rb_num_coerce_bin(x, y); } /* * call-seq: - * fix / numeric => numeric_result - * fix.div(numeric) => numeric_result + * fix / numeric -> numeric_result * * Performs division: the class of the resulting object depends on - * the class of <code>numeric</code> and on the magnitude of the + * the class of <code>numeric</code> and on the magnitude of the * result. */ static VALUE -fix_div(x, y) - VALUE x, y; +fix_div(VALUE x, VALUE y) { - if (FIXNUM_P(y)) { - long div; + return fix_divide(x, y, '/'); +} - fixdivmod(FIX2LONG(x), FIX2LONG(y), &div, 0); - return LONG2NUM(div); - } - return rb_num_coerce_bin(x, y); +/* + * call-seq: + * fix.div(numeric) -> integer + * + * Performs integer division: returns integer value. + */ + +static VALUE +fix_idiv(VALUE x, VALUE y) +{ + return fix_divide(x, y, rb_intern("div")); } /* * call-seq: - * fix % other => Numeric - * fix.modulo(other) => Numeric + * fix % other -> real + * fix.modulo(other) -> real * * Returns <code>fix</code> modulo <code>other</code>. - * See <code>Numeric.divmod</code> for more information. + * See <code>numeric.divmod</code> for more information. */ static VALUE -fix_mod(x, y) - VALUE x, y; +fix_mod(VALUE x, VALUE y) { if (FIXNUM_P(y)) { long mod; @@ -2156,18 +2497,30 @@ fix_mod(x, y) fixdivmod(FIX2LONG(x), FIX2LONG(y), 0, &mod); return LONG2NUM(mod); } - return rb_num_coerce_bin(x, y); + switch (TYPE(y)) { + case T_BIGNUM: + x = rb_int2big(FIX2LONG(x)); + return rb_big_modulo(x, y); + case T_FLOAT: + { + double mod; + + flodivmod((double)FIX2LONG(x), RFLOAT_VALUE(y), 0, &mod); + return DBL2NUM(mod); + } + default: + return rb_num_coerce_bin(x, y, '%'); + } } /* * call-seq: - * fix.divmod(numeric) => array - * + * fix.divmod(numeric) -> array + * * See <code>Numeric#divmod</code>. */ static VALUE -fix_divmod(x, y) - VALUE x, y; +fix_divmod(VALUE x, VALUE y) { if (FIXNUM_P(y)) { long div, mod; @@ -2176,23 +2529,66 @@ fix_divmod(x, y) return rb_assoc_new(LONG2NUM(div), LONG2NUM(mod)); } - return rb_num_coerce_bin(x, y); + switch (TYPE(y)) { + case T_BIGNUM: + x = rb_int2big(FIX2LONG(x)); + return rb_big_divmod(x, y); + case T_FLOAT: + { + double div, mod; + volatile VALUE a, b; + + flodivmod((double)FIX2LONG(x), RFLOAT_VALUE(y), &div, &mod); + a = dbl2ival(div); + b = DBL2NUM(mod); + return rb_assoc_new(a, b); + } + default: + return rb_num_coerce_bin(x, y, rb_intern("divmod")); + } } static VALUE -int_even_p(VALUE num) +int_pow(long x, unsigned long y) { - if (rb_funcall(num, '%', 1, INT2FIX(2)) == INT2FIX(0)) { - return Qtrue; - } - return Qfalse; + int neg = x < 0; + long z = 1; + + if (neg) x = -x; + if (y & 1) + z = x; + else + neg = 0; + y &= ~1; + do { + while (y % 2 == 0) { + if (!FIT_SQRT_LONG(x)) { + VALUE v; + bignum: + v = rb_big_pow(rb_int2big(x), LONG2NUM(y)); + if (z != 1) v = rb_big_mul(rb_int2big(neg ? -z : z), v); + return v; + } + x = x * x; + y >>= 1; + } + { + volatile long xz = x * z; + if (!POSFIXABLE(xz) || xz / x != z) { + goto bignum; + } + z = xz; + } + } while (--y); + if (neg) z = -z; + return LONG2NUM(z); } /* * call-seq: - * fix ** other => Numeric + * fix ** numeric -> numeric_result * - * Raises <code>fix</code> to the <code>other</code> power, which may + * Raises <code>fix</code> to the <code>numeric</code> power, which may * be negative or fractional. * * 2 ** 3 #=> 8 @@ -2201,33 +2597,37 @@ int_even_p(VALUE num) */ static VALUE -fix_pow(x, y) - VALUE x, y; +fix_pow(VALUE x, VALUE y) { long a = FIX2LONG(x); if (FIXNUM_P(y)) { - long b; + long b = FIX2LONG(y); + + if (b < 0) + return rb_funcall(rb_rational_raw1(x), rb_intern("**"), 1, y); - b = FIX2LONG(y); if (b == 0) return INT2FIX(1); if (b == 1) return x; - a = FIX2LONG(x); - if (a == 0) return INT2FIX(0); + if (a == 0) { + if (b > 0) return INT2FIX(0); + return DBL2NUM(INFINITY); + } if (a == 1) return INT2FIX(1); if (a == -1) { if (b % 2 == 0) return INT2FIX(1); - else + else return INT2FIX(-1); } - if (b > 0) { - return rb_big_pow(rb_int2big(a), y); - } - return rb_float_new(pow((double)a, (double)b)); + return int_pow(a, b); } switch (TYPE(y)) { case T_BIGNUM: + + if (rb_funcall(y, '<', 1, INT2FIX(0))) + return rb_funcall(rb_rational_raw1(x), rb_intern("**"), 1, y); + if (a == 0) return INT2FIX(0); if (a == 1) return INT2FIX(1); if (a == -1) { @@ -2237,17 +2637,25 @@ fix_pow(x, y) x = rb_int2big(FIX2LONG(x)); return rb_big_pow(x, y); case T_FLOAT: - if (a == 0) return rb_float_new(0.0); - if (a == 1) return rb_float_new(1.0); - return rb_float_new(pow((double)a, RFLOAT(y)->value)); + if (RFLOAT_VALUE(y) == 0.0) return DBL2NUM(1.0); + if (a == 0) { + return DBL2NUM(RFLOAT_VALUE(y) < 0 ? INFINITY : 0.0); + } + if (a == 1) return DBL2NUM(1.0); + { + double dy = RFLOAT_VALUE(y); + if (a < 0 && dy != round(dy)) + return rb_funcall(rb_complex_raw1(x), rb_intern("**"), 1, y); + return DBL2NUM(pow((double)a, dy)); + } default: - return rb_num_coerce_bin(x, y); + return rb_num_coerce_bin(x, y, rb_intern("**")); } } /* * call-seq: - * fix == other + * fix == other -> true or false * * Return <code>true</code> if <code>fix</code> equals <code>other</code> * numerically. @@ -2257,144 +2665,157 @@ fix_pow(x, y) */ static VALUE -fix_equal(x, y) - VALUE x, y; +fix_equal(VALUE x, VALUE y) { - if (FIXNUM_P(y)) { - return (FIX2LONG(x) == FIX2LONG(y))?Qtrue:Qfalse; - } - else { + if (x == y) return Qtrue; + if (FIXNUM_P(y)) return Qfalse; + switch (TYPE(y)) { + case T_BIGNUM: + return rb_big_eq(y, x); + case T_FLOAT: + return (double)FIX2LONG(x) == RFLOAT_VALUE(y) ? Qtrue : Qfalse; + default: return num_equal(x, y); } } /* * call-seq: - * fix <=> numeric => -1, 0, +1 - * - * Comparison---Returns -1, 0, or +1 depending on whether <i>fix</i> is - * less than, equal to, or greater than <i>numeric</i>. This is the - * basis for the tests in <code>Comparable</code>. + * fix <=> numeric -> -1, 0, +1 or nil + * + * Comparison---Returns -1, 0, +1 or nil depending on whether + * <i>fix</i> is less than, equal to, or greater than + * <i>numeric</i>. This is the basis for the tests in + * <code>Comparable</code>. */ static VALUE -fix_cmp(x, y) - VALUE x, y; +fix_cmp(VALUE x, VALUE y) { + if (x == y) return INT2FIX(0); if (FIXNUM_P(y)) { - long a = FIX2LONG(x), b = FIX2LONG(y); - - if (a == b) return INT2FIX(0); - if (a > b) return INT2FIX(1); + if (FIX2LONG(x) > FIX2LONG(y)) return INT2FIX(1); return INT2FIX(-1); } - else { - return rb_num_coerce_cmp(x, y); + switch (TYPE(y)) { + case T_BIGNUM: + return rb_big_cmp(rb_int2big(FIX2LONG(x)), y); + case T_FLOAT: + return rb_dbl_cmp((double)FIX2LONG(x), RFLOAT_VALUE(y)); + default: + return rb_num_coerce_cmp(x, y, rb_intern("<=>")); } } /* * call-seq: - * fix > other => true or false + * fix > real -> true or false * * Returns <code>true</code> if the value of <code>fix</code> is - * greater than that of <code>other</code>. + * greater than that of <code>real</code>. */ static VALUE -fix_gt(x, y) - VALUE x, y; +fix_gt(VALUE x, VALUE y) { if (FIXNUM_P(y)) { - long a = FIX2LONG(x), b = FIX2LONG(y); - - if (a > b) return Qtrue; + if (FIX2LONG(x) > FIX2LONG(y)) return Qtrue; return Qfalse; } - else { - return rb_num_coerce_relop(x, y); + switch (TYPE(y)) { + case T_BIGNUM: + return FIX2INT(rb_big_cmp(rb_int2big(FIX2LONG(x)), y)) > 0 ? Qtrue : Qfalse; + case T_FLOAT: + return (double)FIX2LONG(x) > RFLOAT_VALUE(y) ? Qtrue : Qfalse; + default: + return rb_num_coerce_relop(x, y, '>'); } } /* * call-seq: - * fix >= other => true or false + * fix >= real -> true or false * * Returns <code>true</code> if the value of <code>fix</code> is - * greater than or equal to that of <code>other</code>. + * greater than or equal to that of <code>real</code>. */ static VALUE -fix_ge(x, y) - VALUE x, y; +fix_ge(VALUE x, VALUE y) { if (FIXNUM_P(y)) { - long a = FIX2LONG(x), b = FIX2LONG(y); - - if (a >= b) return Qtrue; + if (FIX2LONG(x) >= FIX2LONG(y)) return Qtrue; return Qfalse; } - else { - return rb_num_coerce_relop(x, y); + switch (TYPE(y)) { + case T_BIGNUM: + return FIX2INT(rb_big_cmp(rb_int2big(FIX2LONG(x)), y)) >= 0 ? Qtrue : Qfalse; + case T_FLOAT: + return (double)FIX2LONG(x) >= RFLOAT_VALUE(y) ? Qtrue : Qfalse; + default: + return rb_num_coerce_relop(x, y, rb_intern(">=")); } } /* * call-seq: - * fix < other => true or false + * fix < real -> true or false * * Returns <code>true</code> if the value of <code>fix</code> is - * less than that of <code>other</code>. + * less than that of <code>real</code>. */ static VALUE -fix_lt(x, y) - VALUE x, y; +fix_lt(VALUE x, VALUE y) { if (FIXNUM_P(y)) { - long a = FIX2LONG(x), b = FIX2LONG(y); - - if (a < b) return Qtrue; + if (FIX2LONG(x) < FIX2LONG(y)) return Qtrue; return Qfalse; } - else { - return rb_num_coerce_relop(x, y); + switch (TYPE(y)) { + case T_BIGNUM: + return FIX2INT(rb_big_cmp(rb_int2big(FIX2LONG(x)), y)) < 0 ? Qtrue : Qfalse; + case T_FLOAT: + return (double)FIX2LONG(x) < RFLOAT_VALUE(y) ? Qtrue : Qfalse; + default: + return rb_num_coerce_relop(x, y, '<'); } } /* * call-seq: - * fix <= other => true or false + * fix <= real -> true or false * * Returns <code>true</code> if the value of <code>fix</code> is - * less thanor equal to that of <code>other</code>. + * less than or equal to that of <code>real</code>. */ static VALUE -fix_le(x, y) - VALUE x, y; +fix_le(VALUE x, VALUE y) { if (FIXNUM_P(y)) { - long a = FIX2LONG(x), b = FIX2LONG(y); - - if (a <= b) return Qtrue; + if (FIX2LONG(x) <= FIX2LONG(y)) return Qtrue; return Qfalse; } - else { - return rb_num_coerce_relop(x, y); + switch (TYPE(y)) { + case T_BIGNUM: + return FIX2INT(rb_big_cmp(rb_int2big(FIX2LONG(x)), y)) <= 0 ? Qtrue : Qfalse; + case T_FLOAT: + return (double)FIX2LONG(x) <= RFLOAT_VALUE(y) ? Qtrue : Qfalse; + default: + return rb_num_coerce_relop(x, y, rb_intern("<=")); } } /* * call-seq: - * ~fix => integer + * ~fix -> integer * * One's complement: returns a number where each bit is flipped. */ static VALUE -fix_rev(num) - VALUE num; +fix_rev(VALUE num) { long val = FIX2LONG(num); @@ -2402,79 +2823,87 @@ fix_rev(num) return LONG2NUM(val); } +static VALUE +bit_coerce(VALUE x) +{ + while (!FIXNUM_P(x) && TYPE(x) != T_BIGNUM) { + if (TYPE(x) == T_FLOAT) { + rb_raise(rb_eTypeError, "can't convert Float into Integer"); + } + x = rb_to_int(x); + } + return x; +} + /* * call-seq: - * fix & other => integer + * fix & integer -> integer_result * * Bitwise AND. */ static VALUE -fix_and(x, y) - VALUE x, y; +fix_and(VALUE x, VALUE y) { long val; - if (TYPE(y) == T_BIGNUM) { + if (!FIXNUM_P(y = bit_coerce(y))) { return rb_big_and(y, x); } - val = FIX2LONG(x) & NUM2LONG(y); + val = FIX2LONG(x) & FIX2LONG(y); return LONG2NUM(val); } /* * call-seq: - * fix | other => integer + * fix | integer -> integer_result * * Bitwise OR. */ static VALUE -fix_or(x, y) - VALUE x, y; +fix_or(VALUE x, VALUE y) { long val; - if (TYPE(y) == T_BIGNUM) { + if (!FIXNUM_P(y = bit_coerce(y))) { return rb_big_or(y, x); } - val = FIX2LONG(x) | NUM2LONG(y); + val = FIX2LONG(x) | FIX2LONG(y); return LONG2NUM(val); } /* * call-seq: - * fix ^ other => integer + * fix ^ integer -> integer_result * * Bitwise EXCLUSIVE OR. */ static VALUE -fix_xor(x, y) - VALUE x, y; +fix_xor(VALUE x, VALUE y) { long val; - if (TYPE(y) == T_BIGNUM) { + if (!FIXNUM_P(y = bit_coerce(y))) { return rb_big_xor(y, x); } - val = FIX2LONG(x) ^ NUM2LONG(y); + val = FIX2LONG(x) ^ FIX2LONG(y); return LONG2NUM(val); } -static VALUE fix_lshift _((long, unsigned long)); -static VALUE fix_rshift _((long, unsigned long)); +static VALUE fix_lshift(long, unsigned long); +static VALUE fix_rshift(long, unsigned long); /* * call-seq: - * fix << count => integer + * fix << count -> integer * * Shifts _fix_ left _count_ positions (right if _count_ is negative). */ static VALUE -rb_fix_lshift(x, y) - VALUE x, y; +rb_fix_lshift(VALUE x, VALUE y) { long val, width; @@ -2488,12 +2917,10 @@ rb_fix_lshift(x, y) } static VALUE -fix_lshift(val, width) - long val; - unsigned long width; +fix_lshift(long val, unsigned long width) { - if (width > (sizeof(VALUE)*CHAR_BIT-1) - || ((unsigned long)val)>>(sizeof(VALUE)*CHAR_BIT-1-width) > 0) { + if (width > (SIZEOF_LONG*CHAR_BIT-1) + || ((unsigned long)val)>>(SIZEOF_LONG*CHAR_BIT-1-width) > 0) { return rb_big_lshift(rb_int2big(val), ULONG2NUM(width)); } val = val << width; @@ -2502,14 +2929,13 @@ fix_lshift(val, width) /* * call-seq: - * fix >> count => integer + * fix >> count -> integer * * Shifts _fix_ right _count_ positions (left if _count_ is negative). */ static VALUE -rb_fix_rshift(x, y) - VALUE x, y; +rb_fix_rshift(VALUE x, VALUE y) { long i, val; @@ -2536,39 +2962,39 @@ fix_rshift(long val, unsigned long i) /* * call-seq: - * fix[n] => 0, 1 - * + * fix[n] -> 0, 1 + * * Bit Reference---Returns the <em>n</em>th bit in the binary * representation of <i>fix</i>, where <i>fix</i>[0] is the least * significant bit. - * + * * a = 0b11001100101010 * 30.downto(0) do |n| print a[n] end - * + * * <em>produces:</em> - * + * * 0000000000000000011001100101010 */ static VALUE -fix_aref(fix, idx) - VALUE fix, idx; +fix_aref(VALUE fix, VALUE idx) { long val = FIX2LONG(fix); long i; - if (TYPE(idx) == T_BIGNUM) { + idx = rb_to_int(idx); + if (!FIXNUM_P(idx)) { idx = rb_big_norm(idx); if (!FIXNUM_P(idx)) { - if (!RBIGNUM(idx)->sign || val >= 0) + if (!RBIGNUM_SIGN(idx) || val >= 0) return INT2FIX(0); return INT2FIX(1); } } - i = NUM2LONG(idx); + i = FIX2LONG(idx); if (i < 0) return INT2FIX(0); - if (sizeof(VALUE)*CHAR_BIT-1 < i) { + if (SIZEOF_LONG*CHAR_BIT-1 < i) { if (val < 0) return INT2FIX(1); return INT2FIX(0); } @@ -2579,37 +3005,36 @@ fix_aref(fix, idx) /* * call-seq: - * fix.to_f -> float - * + * fix.to_f -> float + * * Converts <i>fix</i> to a <code>Float</code>. - * + * */ static VALUE -fix_to_f(num) - VALUE num; +fix_to_f(VALUE num) { double val; val = (double)FIX2LONG(num); - return rb_float_new(val); + return DBL2NUM(val); } /* * call-seq: - * fix.abs -> aFixnum - * + * fix.abs -> integer + * fix.magnitude -> integer + * * Returns the absolute value of <i>fix</i>. - * + * * -12345.abs #=> 12345 * 12345.abs #=> 12345 - * + * */ static VALUE -fix_abs(fix) - VALUE fix; +fix_abs(VALUE fix) { long i = FIX2LONG(fix); @@ -2618,93 +3043,47 @@ fix_abs(fix) return LONG2NUM(i); } -/* - * call-seq: - * fix.id2name -> string or nil - * - * Returns the name of the object whose symbol id is <i>fix</i>. If - * there is no symbol in the symbol table with this value, returns - * <code>nil</code>. <code>id2name</code> has nothing to do with the - * <code>Object.id</code> method. See also <code>Fixnum#to_sym</code>, - * <code>String#intern</code>, and class <code>Symbol</code>. - * - * symbol = :@inst_var #=> :@inst_var - * id = symbol.to_i #=> 9818 - * id.id2name #=> "@inst_var" - */ - -static VALUE -fix_id2name(fix) - VALUE fix; -{ - char *name = rb_id2name(FIX2UINT(fix)); - if (name) return rb_str_new2(name); - return Qnil; -} - - -/* - * call-seq: - * fix.to_sym -> aSymbol - * - * Returns the symbol whose integer value is <i>fix</i>. See also - * <code>Fixnum#id2name</code>. - * - * fred = :fred.to_i - * fred.id2name #=> "fred" - * fred.to_sym #=> :fred - */ - -static VALUE -fix_to_sym(fix) - VALUE fix; -{ - ID id = FIX2UINT(fix); - - if (rb_id2name(id)) { - return ID2SYM(id); - } - return Qnil; -} /* * call-seq: - * fix.size -> fixnum - * + * fix.size -> fixnum + * * Returns the number of <em>bytes</em> in the machine representation * of a <code>Fixnum</code>. - * + * * 1.size #=> 4 * -1.size #=> 4 * 2147483647.size #=> 4 */ static VALUE -fix_size(fix) - VALUE fix; +fix_size(VALUE fix) { return INT2FIX(sizeof(long)); } /* * call-seq: - * int.upto(limit) {|i| block } => int - * + * int.upto(limit) {|i| block } -> self + * int.upto(limit) -> an_enumerator + * * Iterates <em>block</em>, passing in integer values from <i>int</i> * up to and including <i>limit</i>. - * + * + * If no block is given, an enumerator is returned instead. + * * 5.upto(10) { |i| print i, " " } - * + * * <em>produces:</em> - * + * * 5 6 7 8 9 10 */ static VALUE -int_upto(from, to) - VALUE from, to; +int_upto(VALUE from, VALUE to) { + RETURN_ENUMERATOR(from, 1, &to); if (FIXNUM_P(from) && FIXNUM_P(to)) { long i, end; @@ -2727,23 +3106,26 @@ int_upto(from, to) /* * call-seq: - * int.downto(limit) {|i| block } => int - * + * int.downto(limit) {|i| block } -> self + * int.downto(limit) -> an_enumerator + * * Iterates <em>block</em>, passing decreasing values from <i>int</i> * down to and including <i>limit</i>. - * + * + * If no block is given, an enumerator is returned instead. + * * 5.downto(1) { |n| print n, ".. " } * print " Liftoff!\n" - * + * * <em>produces:</em> - * + * * 5.. 4.. 3.. 2.. 1.. Liftoff! */ static VALUE -int_downto(from, to) - VALUE from, to; +int_downto(VALUE from, VALUE to) { + RETURN_ENUMERATOR(from, 1, &to); if (FIXNUM_P(from) && FIXNUM_P(to)) { long i, end; @@ -2766,24 +3148,28 @@ int_downto(from, to) /* * call-seq: - * int.times {|i| block } => int - * + * int.times {|i| block } -> self + * int.times -> an_enumerator + * * Iterates block <i>int</i> times, passing in values from zero to * <i>int</i> - 1. - * + * + * If no block is given, an enumerator is returned instead. + * * 5.times do |i| * print i, " " * end - * + * * <em>produces:</em> - * + * * 0 1 2 3 4 */ static VALUE -int_dotimes(num) - VALUE num; +int_dotimes(VALUE num) { + RETURN_ENUMERATOR(num, 0, 0); + if (FIXNUM_P(num)) { long i, end; @@ -2806,15 +3192,64 @@ int_dotimes(num) /* * call-seq: - * fix.zero? => true or false - * + * num.round([ndigits]) -> integer or float + * + * Rounds <i>flt</i> to a given precision in decimal digits (default 0 digits). + * Precision may be negative. Returns a floating point number when +ndigits+ + * is positive, +self+ for zero, and round down for negative. + * + * 1.round #=> 1 + * 1.round(2) #=> 1.0 + * 15.round(-1) #=> 20 + */ + +static VALUE +int_round(int argc, VALUE* argv, VALUE num) +{ + VALUE n, f, h, r; + int ndigits; + + if (argc == 0) return num; + rb_scan_args(argc, argv, "1", &n); + ndigits = NUM2INT(n); + if (ndigits > 0) { + return rb_Float(num); + } + if (ndigits == 0) { + return num; + } + ndigits = -ndigits; + if (ndigits < 0) { + rb_raise(rb_eArgError, "ndigits out of range"); + } + f = int_pow(10, ndigits); + if (FIXNUM_P(num) && FIXNUM_P(f)) { + SIGNED_VALUE x = FIX2LONG(num), y = FIX2LONG(f); + int neg = x < 0; + if (neg) x = -x; + x = (x + y / 2) / y * y; + if (neg) x = -x; + return LONG2NUM(x); + } + h = rb_funcall(f, '/', 1, INT2FIX(2)); + r = rb_funcall(num, '%', 1, f); + n = rb_funcall(num, '-', 1, r); + if (!RTEST(rb_funcall(r, '<', 1, h))) { + n = rb_funcall(n, '+', 1, f); + } + return n; +} + +/* + * call-seq: + * fix.zero? -> true or false + * * Returns <code>true</code> if <i>fix</i> is zero. - * + * */ static VALUE -fix_zero_p(num) - VALUE num; +fix_zero_p(VALUE num) { if (FIX2LONG(num) == 0) { return Qtrue; @@ -2822,9 +3257,76 @@ fix_zero_p(num) return Qfalse; } +/* + * call-seq: + * fix.odd? -> true or false + * + * Returns <code>true</code> if <i>fix</i> is an odd number. + */ + +static VALUE +fix_odd_p(VALUE num) +{ + if (num & 2) { + return Qtrue; + } + return Qfalse; +} + +/* + * call-seq: + * fix.even? -> true or false + * + * Returns <code>true</code> if <i>fix</i> is an even number. + */ + +static VALUE +fix_even_p(VALUE num) +{ + if (num & 2) { + return Qfalse; + } + return Qtrue; +} + +/* + * Document-class: ZeroDivisionError + * + * Raised when attempting to divide an integer by 0. + * + * 42 / 0 + * + * <em>raises the exception:</em> + * + * ZeroDivisionError: divided by 0 + * + * Note that only division by an exact 0 will raise that exception: + * + * 42 / 0.0 #=> Float::INFINITY + * 42 / -0.0 #=> -Float::INFINITY + * 0 / 0.0 #=> NaN + */ + +/* + * Document-class: FloatDomainError + * + * Raised when attempting to convert special float values + * (in particular infinite or NaN) + * to numerical classes which don't support them. + * + * Float::INFINITY.to_r + * + * <em>raises the exception:</em> + * + * FloatDomainError: Infinity + */ + void -Init_Numeric() +Init_Numeric(void) { +#undef rb_intern +#define rb_intern(str) rb_intern_const(str) + #if defined(__FreeBSD__) && __FreeBSD__ < 4 /* allow divide by zero -- Inf */ fpsetmask(fpgetmask() & ~(FP_X_DZ|FP_X_INV|FP_X_OFL)); @@ -2848,25 +3350,30 @@ Init_Numeric() rb_define_method(rb_cNumeric, "initialize_copy", num_init_copy, 1); rb_define_method(rb_cNumeric, "coerce", num_coerce, 1); + rb_define_method(rb_cNumeric, "i", num_imaginary, 0); rb_define_method(rb_cNumeric, "+@", num_uplus, 0); rb_define_method(rb_cNumeric, "-@", num_uminus, 0); rb_define_method(rb_cNumeric, "<=>", num_cmp, 1); rb_define_method(rb_cNumeric, "eql?", num_eql, 1); rb_define_method(rb_cNumeric, "quo", num_quo, 1); + rb_define_method(rb_cNumeric, "fdiv", num_fdiv, 1); rb_define_method(rb_cNumeric, "div", num_div, 1); rb_define_method(rb_cNumeric, "divmod", num_divmod, 1); + rb_define_method(rb_cNumeric, "%", num_modulo, 1); rb_define_method(rb_cNumeric, "modulo", num_modulo, 1); rb_define_method(rb_cNumeric, "remainder", num_remainder, 1); rb_define_method(rb_cNumeric, "abs", num_abs, 0); + rb_define_method(rb_cNumeric, "magnitude", num_abs, 0); rb_define_method(rb_cNumeric, "to_int", num_to_int, 0); + rb_define_method(rb_cNumeric, "real?", num_real_p, 0); rb_define_method(rb_cNumeric, "integer?", num_int_p, 0); rb_define_method(rb_cNumeric, "zero?", num_zero_p, 0); rb_define_method(rb_cNumeric, "nonzero?", num_nonzero_p, 0); rb_define_method(rb_cNumeric, "floor", num_floor, 0); rb_define_method(rb_cNumeric, "ceil", num_ceil, 0); - rb_define_method(rb_cNumeric, "round", num_round, 0); + rb_define_method(rb_cNumeric, "round", num_round, -1); rb_define_method(rb_cNumeric, "truncate", num_truncate, 0); rb_define_method(rb_cNumeric, "step", num_step, -1); @@ -2875,45 +3382,44 @@ Init_Numeric() rb_undef_method(CLASS_OF(rb_cInteger), "new"); rb_define_method(rb_cInteger, "integer?", int_int_p, 0); + rb_define_method(rb_cInteger, "odd?", int_odd_p, 0); + rb_define_method(rb_cInteger, "even?", int_even_p, 0); rb_define_method(rb_cInteger, "upto", int_upto, 1); rb_define_method(rb_cInteger, "downto", int_downto, 1); rb_define_method(rb_cInteger, "times", int_dotimes, 0); - rb_include_module(rb_cInteger, rb_mPrecision); rb_define_method(rb_cInteger, "succ", int_succ, 0); rb_define_method(rb_cInteger, "next", int_succ, 0); - rb_define_method(rb_cInteger, "chr", int_chr, 0); + rb_define_method(rb_cInteger, "pred", int_pred, 0); + rb_define_method(rb_cInteger, "chr", int_chr, -1); + rb_define_method(rb_cInteger, "ord", int_ord, 0); rb_define_method(rb_cInteger, "to_i", int_to_i, 0); rb_define_method(rb_cInteger, "to_int", int_to_i, 0); rb_define_method(rb_cInteger, "floor", int_to_i, 0); rb_define_method(rb_cInteger, "ceil", int_to_i, 0); - rb_define_method(rb_cInteger, "round", int_to_i, 0); rb_define_method(rb_cInteger, "truncate", int_to_i, 0); + rb_define_method(rb_cInteger, "round", int_round, -1); rb_cFixnum = rb_define_class("Fixnum", rb_cInteger); - rb_include_module(rb_cFixnum, rb_mPrecision); - rb_define_singleton_method(rb_cFixnum, "induced_from", rb_fix_induced_from, 1); - rb_define_singleton_method(rb_cInteger, "induced_from", rb_int_induced_from, 1); rb_define_method(rb_cFixnum, "to_s", fix_to_s, -1); - rb_define_method(rb_cFixnum, "id2name", fix_id2name, 0); - rb_define_method(rb_cFixnum, "to_sym", fix_to_sym, 0); - rb_define_method(rb_cFixnum, "-@", fix_uminus, 0); rb_define_method(rb_cFixnum, "+", fix_plus, 1); rb_define_method(rb_cFixnum, "-", fix_minus, 1); rb_define_method(rb_cFixnum, "*", fix_mul, 1); rb_define_method(rb_cFixnum, "/", fix_div, 1); - rb_define_method(rb_cFixnum, "div", fix_div, 1); + rb_define_method(rb_cFixnum, "div", fix_idiv, 1); rb_define_method(rb_cFixnum, "%", fix_mod, 1); rb_define_method(rb_cFixnum, "modulo", fix_mod, 1); rb_define_method(rb_cFixnum, "divmod", fix_divmod, 1); - rb_define_method(rb_cFixnum, "quo", fix_quo, 1); + rb_define_method(rb_cFixnum, "fdiv", fix_fdiv, 1); rb_define_method(rb_cFixnum, "**", fix_pow, 1); rb_define_method(rb_cFixnum, "abs", fix_abs, 0); + rb_define_method(rb_cFixnum, "magnitude", fix_abs, 0); rb_define_method(rb_cFixnum, "==", fix_equal, 1); + rb_define_method(rb_cFixnum, "===", fix_equal, 1); rb_define_method(rb_cFixnum, "<=>", fix_cmp, 1); rb_define_method(rb_cFixnum, ">", fix_gt, 1); rb_define_method(rb_cFixnum, ">=", fix_ge, 1); @@ -2932,15 +3438,15 @@ Init_Numeric() rb_define_method(rb_cFixnum, "to_f", fix_to_f, 0); rb_define_method(rb_cFixnum, "size", fix_size, 0); rb_define_method(rb_cFixnum, "zero?", fix_zero_p, 0); + rb_define_method(rb_cFixnum, "odd?", fix_odd_p, 0); + rb_define_method(rb_cFixnum, "even?", fix_even_p, 0); + rb_define_method(rb_cFixnum, "succ", fix_succ, 0); rb_cFloat = rb_define_class("Float", rb_cNumeric); rb_undef_alloc_func(rb_cFloat); rb_undef_method(CLASS_OF(rb_cFloat), "new"); - rb_define_singleton_method(rb_cFloat, "induced_from", rb_flo_induced_from, 1); - rb_include_module(rb_cFloat, rb_mPrecision); - rb_define_const(rb_cFloat, "ROUNDS", INT2FIX(FLT_ROUNDS)); rb_define_const(rb_cFloat, "RADIX", INT2FIX(FLT_RADIX)); rb_define_const(rb_cFloat, "MANT_DIG", INT2FIX(DBL_MANT_DIG)); @@ -2949,9 +3455,11 @@ Init_Numeric() rb_define_const(rb_cFloat, "MAX_EXP", INT2FIX(DBL_MAX_EXP)); rb_define_const(rb_cFloat, "MIN_10_EXP", INT2FIX(DBL_MIN_10_EXP)); rb_define_const(rb_cFloat, "MAX_10_EXP", INT2FIX(DBL_MAX_10_EXP)); - rb_define_const(rb_cFloat, "MIN", rb_float_new(DBL_MIN)); - rb_define_const(rb_cFloat, "MAX", rb_float_new(DBL_MAX)); - rb_define_const(rb_cFloat, "EPSILON", rb_float_new(DBL_EPSILON)); + rb_define_const(rb_cFloat, "MIN", DBL2NUM(DBL_MIN)); + rb_define_const(rb_cFloat, "MAX", DBL2NUM(DBL_MAX)); + rb_define_const(rb_cFloat, "EPSILON", DBL2NUM(DBL_EPSILON)); + rb_define_const(rb_cFloat, "INFINITY", DBL2NUM(INFINITY)); + rb_define_const(rb_cFloat, "NAN", DBL2NUM(NAN)); rb_define_method(rb_cFloat, "to_s", flo_to_s, 0); rb_define_method(rb_cFloat, "coerce", flo_coerce, 1); @@ -2960,11 +3468,14 @@ Init_Numeric() rb_define_method(rb_cFloat, "-", flo_minus, 1); rb_define_method(rb_cFloat, "*", flo_mul, 1); rb_define_method(rb_cFloat, "/", flo_div, 1); + rb_define_method(rb_cFloat, "quo", flo_quo, 1); + rb_define_method(rb_cFloat, "fdiv", flo_quo, 1); rb_define_method(rb_cFloat, "%", flo_mod, 1); rb_define_method(rb_cFloat, "modulo", flo_mod, 1); rb_define_method(rb_cFloat, "divmod", flo_divmod, 1); rb_define_method(rb_cFloat, "**", flo_pow, 1); rb_define_method(rb_cFloat, "==", flo_eq, 1); + rb_define_method(rb_cFloat, "===", flo_eq, 1); rb_define_method(rb_cFloat, "<=>", flo_cmp, 1); rb_define_method(rb_cFloat, ">", flo_gt, 1); rb_define_method(rb_cFloat, ">=", flo_ge, 1); @@ -2974,13 +3485,14 @@ Init_Numeric() rb_define_method(rb_cFloat, "hash", flo_hash, 0); rb_define_method(rb_cFloat, "to_f", flo_to_f, 0); rb_define_method(rb_cFloat, "abs", flo_abs, 0); + rb_define_method(rb_cFloat, "magnitude", flo_abs, 0); rb_define_method(rb_cFloat, "zero?", flo_zero_p, 0); rb_define_method(rb_cFloat, "to_i", flo_truncate, 0); rb_define_method(rb_cFloat, "to_int", flo_truncate, 0); rb_define_method(rb_cFloat, "floor", flo_floor, 0); rb_define_method(rb_cFloat, "ceil", flo_ceil, 0); - rb_define_method(rb_cFloat, "round", flo_round, 0); + rb_define_method(rb_cFloat, "round", flo_round, -1); rb_define_method(rb_cFloat, "truncate", flo_truncate, 0); rb_define_method(rb_cFloat, "nan?", flo_is_nan_p, 0); |
