Files
mruby-mruby/src/numeric.c
T
Yukihiro "Matz" Matsumoto db8368fa86 numeric.c: remove bit operations methods from Float class; #6197
mruby used to use float numbers for overflown integers before we
implemented big integers. Now we don't need bit operations for float
numbers anymore. Also removed tests for shift operations for float
numbers.
2024-03-11 11:44:41 +09:00

2174 lines
52 KiB
C

/*
** numeric.c - Numeric, Integer, Float class
**
** See Copyright Notice in mruby.h
*/
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/numeric.h>
#include <mruby/string.h>
#include <mruby/class.h>
#include <mruby/internal.h>
#include <mruby/presym.h>
#include <string.h>
#ifndef MRB_NO_FLOAT
#ifdef MRB_USE_FLOAT32
#define trunc(f) truncf(f)
#define fmod(x,y) fmodf(x,y)
#else
#endif
#endif
mrb_noreturn void
mrb_int_overflow(mrb_state *mrb, const char *reason)
{
mrb_raisef(mrb, E_RANGE_ERROR, "integer overflow in %s", reason);
}
mrb_noreturn void
mrb_int_zerodiv(mrb_state *mrb)
{
mrb_raise(mrb, E_ZERODIV_ERROR, "divided by 0");
}
static mrb_noreturn void
mrb_int_noconv(mrb_state *mrb, mrb_value y)
{
mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y into Integer", y);
}
mrb_value
mrb_int_pow(mrb_state *mrb, mrb_value x, mrb_value y)
{
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
#ifndef MRB_NO_FLOAT
if (mrb_float_p(y)) {
return mrb_float_value(mrb, pow(mrb_bint_as_float(mrb, x), mrb_float(y)));
}
#endif
return mrb_bint_pow(mrb, x, y);
}
#endif
mrb_int base = mrb_integer(x);
mrb_int result = 1;
mrb_int exp;
#ifndef MRB_NO_FLOAT
if (mrb_float_p(y)) {
return mrb_float_value(mrb, pow((double)base, mrb_float(y)));
}
else if (mrb_integer_p(y)) {
exp = mrb_integer(y);
}
else
#endif
{
exp = mrb_as_int(mrb, y);
}
if (exp < 0) {
#ifndef MRB_NO_FLOAT
return mrb_float_value(mrb, pow((double)base, (double)exp));
#else
mrb_int_overflow(mrb, "negative power");
#endif
}
for (;;) {
if (exp & 1) {
if (mrb_int_mul_overflow(result, base, &result)) {
#ifdef MRB_USE_BIGINT
return mrb_bint_pow(mrb, mrb_bint_new_int(mrb, mrb_integer(x)), y);
#else
mrb_int_overflow(mrb, "power");
#endif
}
}
exp >>= 1;
if (exp == 0) break;
if (mrb_int_mul_overflow(base, base, &base)) {
#ifdef MRB_USE_BIGINT
return mrb_bint_pow(mrb, mrb_bint_new_int(mrb, mrb_integer(x)), y);
#else
mrb_int_overflow(mrb, "power");
#endif
}
}
return mrb_int_value(mrb, result);
}
/*
* call-seq:
*
* num ** other -> num
*
* Raises <code>num</code> the <code>other</code> power.
*
* 2.0**3 #=> 8.0
*/
static mrb_value
int_pow(mrb_state *mrb, mrb_value x)
{
return mrb_int_pow(mrb, x, mrb_get_arg1(mrb));
}
mrb_int
mrb_div_int(mrb_int x, mrb_int y)
{
mrb_int div = x / y;
if ((x ^ y) < 0 && x != div * y) {
div -= 1;
}
return div;
}
mrb_value
mrb_div_int_value(mrb_state *mrb, mrb_int x, mrb_int y)
{
if (y == 0) {
mrb_int_zerodiv(mrb);
}
else if (x == MRB_INT_MIN && y == -1) {
#ifdef MRB_USE_BIGINT
return mrb_bint_mul_ii(mrb, x, y);
#else
mrb_int_overflow(mrb, "division");
#endif
}
return mrb_int_value(mrb, mrb_div_int(x, y));
}
/* 15.2.8.3.6 */
/*
* call-seq:
* int / num -> num
*
* Performs division: the class of the resulting object depends on
* the class of <code>num</code> and on the magnitude of the
* result.
*/
static mrb_value
int_div(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
return mrb_bint_div(mrb, x, y);
}
#endif
mrb_int a = mrb_integer(x);
if (mrb_integer_p(y)) {
return mrb_div_int_value(mrb, a, mrb_integer(y));
}
switch (mrb_type(y)) {
#ifdef MRB_USE_BIGINT
case MRB_TT_BIGINT:
return mrb_bint_div(mrb, mrb_bint_new_int(mrb, a), y);
#endif
#ifdef MRB_USE_RATIONAL
case MRB_TT_RATIONAL:
return mrb_rational_div(mrb, mrb_rational_new(mrb, a, 1), y);
#endif
#ifdef MRB_USE_COMPLEX
case MRB_TT_COMPLEX:
x = mrb_complex_new(mrb, (mrb_float)a, 0);
return mrb_complex_div(mrb, x, y);
#endif
#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
return mrb_float_value(mrb, mrb_div_float((mrb_float)a, mrb_as_float(mrb, y)));
#endif
default:
mrb_int_noconv(mrb, y);
}
}
/* 15.2.9.3.19(x) */
/*
* call-seq:
* num.quo(numeric) -> real
*
* Returns most exact division.
*/
/*
* call-seq:
* int.div(other) -> int
*
* Performs division: resulting integer.
*/
static mrb_value
int_idiv(mrb_state *mrb, mrb_value x)
{
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
return mrb_bint_div(mrb, x, mrb_get_arg1(mrb));
}
#endif
mrb_int y = mrb_as_int(mrb, mrb_get_arg1(mrb));
return mrb_div_int_value(mrb, mrb_integer(x), y);
}
static mrb_value
int_quo(mrb_state *mrb, mrb_value x)
{
#ifndef MRB_USE_RATIONAL
#ifdef MRB_NO_FLOAT
return int_idiv(mrb, x);
#else
mrb_float y = mrb_as_float(mrb, mrb_get_arg1(mrb));
if (y == 0) {
mrb_int_zerodiv(mrb);
}
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
return mrb_float_value(mrb, mrb_bint_as_float(mrb, x) / y);
}
#endif
return mrb_float_value(mrb, mrb_integer(x) / y);
#endif
#else
mrb_int a = mrb_integer(x);
mrb_value y = mrb_get_arg1(mrb);
if (mrb_integer_p(y) && mrb_class_defined_id(mrb, MRB_SYM(Rational))) {
return mrb_rational_new(mrb, a, mrb_integer(y));
}
switch (mrb_type(y)) {
case MRB_TT_RATIONAL:
x = mrb_rational_new(mrb, a, 1);
return mrb_rational_div(mrb, x, y);
default:
#ifndef MRB_NO_FLOAT
return mrb_float_value(mrb, mrb_div_float((mrb_float)a, mrb_as_float(mrb, y)));
#else
mrb_int_noconv(mrb, y);
break;
#endif
}
#endif
}
static mrb_value
coerce_step_counter(mrb_state *mrb, mrb_value self)
{
mrb_value num, step;
mrb_get_args(mrb, "oo", &num, &step);
#ifndef MRB_NO_FLOAT
mrb->c->ci->mid = 0;
if (mrb_float_p(num) || mrb_float_p(step)) {
return mrb_ensure_float_type(mrb, self);
}
#endif
return self;
}
#ifndef MRB_NO_FLOAT
/********************************************************************
*
* Document-class: Float
*
* <code>Float</code> objects represent inexact real numbers using
* the native architecture's double-precision floating-point
* representation.
*/
static mrb_value
flo_pow(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
mrb_float d = pow(mrb_as_float(mrb, x), mrb_as_float(mrb, y));
return mrb_float_value(mrb, d);
}
static mrb_value
flo_idiv(mrb_state *mrb, mrb_value xv)
{
mrb_float x = mrb_float(xv);
mrb_check_num_exact(mrb, x);
mrb_int y = mrb_as_int(mrb, mrb_get_arg1(mrb));
return mrb_div_int_value(mrb, (mrb_int)x, y);
}
mrb_float
mrb_div_float(mrb_float x, mrb_float y)
{
if (y != 0.0) {
return x / y;
}
else if (x == 0.0) {
return NAN;
}
else {
return x * (signbit(y) ? -1.0 : 1.0) * INFINITY;
}
}
/* 15.2.9.3.6 */
/*
* call-seq:
* float / num -> float
*
* Returns a new Float which is the result of dividing float by num.
*/
static mrb_value
flo_div(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
mrb_float a = mrb_float(x);
switch(mrb_type(y)) {
#ifdef MRB_USE_COMPLEX
case MRB_TT_COMPLEX:
return mrb_complex_div(mrb, mrb_complex_new(mrb, a, 0), y);
#endif
case MRB_TT_FLOAT:
a = mrb_div_float(a, mrb_float(y));
return mrb_float_value(mrb, a);
default:
a = mrb_div_float(a, mrb_as_float(mrb, y));
return mrb_float_value(mrb, a);
}
return mrb_float_value(mrb, a);
}
/* the argument `fmt` is no longer used; you can pass `NULL` */
mrb_value
mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
{
char buf[25];
#ifdef MRB_USE_FLOAT32
const int prec = 7;
#else
const int prec = 15;
#endif
mrb_format_float(mrb_float(flo), buf, sizeof(buf), 'g', prec, '\0');
for (char *p = buf; *p; p++) {
if (*p == '.') goto exit;
if (*p == 'e') {
memmove(p+2, p, strlen(p)+1);
p[0] = '.';
p[1] = '0';
goto exit;
}
}
strcat(buf, ".0");
exit:
return mrb_str_new_cstr(mrb, buf);
}
/* 15.2.9.3.16(x) */
/*
* call-seq:
* flt.to_s -> string
* flt.inspect -> 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
* "<code>-Infinity</code>".
*
* 3.0.to_s #=> 3.0
* 3.25.to_s #=> 3.25
*/
static mrb_value
flo_to_s(mrb_state *mrb, mrb_value flt)
{
mrb_float f = mrb_float(flt);
mrb_value str;
if (isinf(f)) {
str = f < 0 ? mrb_str_new_lit(mrb, "-Infinity")
: mrb_str_new_lit(mrb, "Infinity");
}
else if (isnan(f)) {
str = mrb_str_new_lit(mrb, "NaN");
}
else {
str = mrb_float_to_str(mrb, flt, NULL);
}
RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
return str;
}
/* 15.2.9.3.3 */
/*
* call-seq:
* float + other -> float
*
* Returns a new float which is the sum of <code>float</code>
* and <code>other</code>.
*/
static mrb_value
flo_add(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
mrb_float a = mrb_float(x);
switch (mrb_type(y)) {
case MRB_TT_FLOAT:
return mrb_float_value(mrb, a + mrb_float(y));
#if defined(MRB_USE_COMPLEX)
case MRB_TT_COMPLEX:
return mrb_complex_add(mrb, y, x);
#endif
default:
return mrb_float_value(mrb, a + mrb_as_float(mrb, y));
}
}
/* 15.2.9.3.4 */
/*
* call-seq:
* float - other -> float
*
* Returns a new float which is the difference of <code>float</code>
* and <code>other</code>.
*/
static mrb_value
flo_sub(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
mrb_float a = mrb_float(x);
switch (mrb_type(y)) {
case MRB_TT_FLOAT:
return mrb_float_value(mrb, a - mrb_float(y));
#if defined(MRB_USE_COMPLEX)
case MRB_TT_COMPLEX:
return mrb_complex_sub(mrb, mrb_complex_new(mrb, a, 0), y);
#endif
default:
return mrb_float_value(mrb, a - mrb_as_float(mrb, y));
}
}
/* 15.2.9.3.5 */
/*
* call-seq:
* float * other -> float
*
* Returns a new float which is the product of <code>float</code>
* and <code>other</code>.
*/
static mrb_value
flo_mul(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
mrb_float a = mrb_float(x);
switch (mrb_type(y)) {
case MRB_TT_FLOAT:
return mrb_float_value(mrb, a * mrb_float(y));
#if defined(MRB_USE_COMPLEX)
case MRB_TT_COMPLEX:
return mrb_complex_mul(mrb, y, x);
#endif
default:
return mrb_float_value(mrb, a * mrb_as_float(mrb, y));
}
}
static void
flodivmod(mrb_state *mrb, double x, double y, mrb_float *divp, mrb_float *modp)
{
double div, mod;
if (isnan(y)) {
/* y is NaN so all results are NaN */
div = mod = y;
goto exit;
}
if (y == 0.0) {
mrb_int_zerodiv(mrb);
}
if (isinf(y) && !isinf(x)) {
mod = x;
}
else {
mod = fmod(x, y);
}
if (isinf(x) && !isinf(y)) {
div = x;
}
else {
div = (x - mod) / y;
if (modp && divp) div = round(div);
}
if (div == 0) div = 0.0;
if (mod == 0) mod = 0.0;
if (y*mod < 0) {
mod += y;
div -= 1.0;
}
exit:
if (modp) *modp = mod;
if (divp) *divp = div;
}
/* 15.2.9.3.5 */
/*
* call-seq:
* 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 mrb_value
flo_mod(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
mrb_float mod;
flodivmod(mrb, mrb_float(x), mrb_as_float(mrb, y), NULL, &mod);
return mrb_float_value(mrb, mod);
}
#endif
/* 15.2.8.3.16 */
/*
* call-seq:
* 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 mrb_value
num_eql(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
return mrb_bool_value(mrb_bint_cmp(mrb, x, y) == 0);
}
#endif
#ifndef MRB_NO_FLOAT
if (mrb_float_p(x)) {
if (!mrb_float_p(y)) return mrb_false_value();
return mrb_bool_value(mrb_float(x) == mrb_float(y));
}
#endif
if (mrb_integer_p(x)) {
if (!mrb_integer_p(y)) return mrb_false_value();
return mrb_bool_value(mrb_integer(x) == mrb_integer(y));
}
return mrb_bool_value(mrb_equal(mrb, x, y));
}
#ifndef MRB_NO_FLOAT
/* 15.2.9.3.7 */
/*
* call-seq:
* 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 mrb_value
flo_eq(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
switch (mrb_type(y)) {
case MRB_TT_INTEGER:
return mrb_bool_value(mrb_float(x) == (mrb_float)mrb_integer(y));
case MRB_TT_FLOAT:
return mrb_bool_value(mrb_float(x) == mrb_float(y));
#ifdef MRB_USE_RATIONAL
case MRB_TT_RATIONAL:
return mrb_bool_value(mrb_float(x) == mrb_as_float(mrb, y));
#endif
#ifdef MRB_USE_COMPLEX
case MRB_TT_COMPLEX:
return mrb_bool_value(mrb_equal(mrb, y, x));
#endif
default:
return mrb_false_value();
}
}
/* 15.2.9.3.13 */
/*
* Document-method: Float#to_f
*
* call-seq:
* flt.to_f -> self
*
* As <code>flt</code> is already a float, returns +self+.
*/
/* 15.2.9.3.11 */
/*
* call-seq:
* 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 mrb_value
flo_infinite_p(mrb_state *mrb, mrb_value num)
{
mrb_float value = mrb_float(num);
if (isinf(value)) {
return mrb_fixnum_value(value < 0 ? -1 : 1);
}
return mrb_nil_value();
}
/* 15.2.9.3.9 */
/*
* call-seq:
* 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 mrb_value
flo_finite_p(mrb_state *mrb, mrb_value num)
{
return mrb_bool_value(isfinite(mrb_float(num)));
}
/*
* 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_i
*
* <em>raises the exception:</em>
*
* FloatDomainError: Infinity
*/
/* ------------------------------------------------------------------------*/
void
mrb_check_num_exact(mrb_state *mrb, mrb_float num)
{
if (isinf(num)) {
mrb_raise(mrb, E_FLOATDOMAIN_ERROR, num < 0 ? "-Infinity" : "Infinity");
}
if (isnan(num)) {
mrb_raise(mrb, E_FLOATDOMAIN_ERROR, "NaN");
}
}
static mrb_value
flo_rounding_int(mrb_state *mrb, mrb_float f)
{
if (!FIXABLE_FLOAT(f)) {
#ifdef MRB_USE_BIGINT
return mrb_bint_new_float(mrb, f);
#else
mrb_int_overflow(mrb, "rounding");
#endif
}
return mrb_int_value(mrb, (mrb_int)f);
}
static mrb_value
flo_rounding(mrb_state *mrb, mrb_value num, double (*func)(double))
{
mrb_float f = mrb_float(num);
mrb_int ndigits = 0;
#ifdef MRB_USE_FLOAT32
const int fprec = 7;
#else
const int fprec = 15;
#endif
mrb_get_args(mrb, "|i", &ndigits);
if (f == 0.0) {
return ndigits > 0 ? mrb_float_value(mrb, f) : mrb_fixnum_value(0);
}
if (ndigits > 0) {
if (ndigits > fprec) return num;
mrb_float d = pow(10, (double)ndigits);
f = func(f * d) / d;
mrb_check_num_exact(mrb, f);
return mrb_float_value(mrb, f);
}
if (ndigits < 0) {
mrb_float d = pow(10, -(double)ndigits);
f = func(f / d) * d;
}
else { /* ndigits == 0 */
f = func(f);
}
mrb_check_num_exact(mrb, f);
return flo_rounding_int(mrb, f);
}
/* 15.2.9.3.10 */
/*
* call-seq:
* float.floor([ndigits]) -> integer or float
*
* Returns the largest number less than or equal to +float+ with
* a precision of +ndigits+ decimal digits (default: 0).
*
* When the precision is negative, the returned value is an integer
* with at least <code>ndigits.abs</code> trailing zeros.
*
* Returns a floating-point number when +ndigits+ is positive,
* otherwise returns an integer.
*
* 1.2.floor #=> 1
* 2.0.floor #=> 2
* (-1.2).floor #=> -2
* (-2.0).floor #=> -2
*
* 1.234567.floor(2) #=> 1.23
* 1.234567.floor(3) #=> 1.234
* 1.234567.floor(4) #=> 1.2345
* 1.234567.floor(5) #=> 1.23456
*
* 34567.89.floor(-5) #=> 0
* 34567.89.floor(-4) #=> 30000
* 34567.89.floor(-3) #=> 34000
* 34567.89.floor(-2) #=> 34500
* 34567.89.floor(-1) #=> 34560
* 34567.89.floor(0) #=> 34567
* 34567.89.floor(1) #=> 34567.8
* 34567.89.floor(2) #=> 34567.89
* 34567.89.floor(3) #=> 34567.89
*
* Note that the limited precision of floating-point arithmetic
* might lead to surprising results:
*
* (0.3 / 0.1).floor #=> 2 (!)
*/
static mrb_value
flo_floor(mrb_state *mrb, mrb_value num)
{
return flo_rounding(mrb, num, floor);
}
/* 15.2.9.3.8 */
/*
* call-seq:
* float.ceil([ndigits]) -> integer or float
*
* Returns the smallest number greater than or equal to +float+ with
* a precision of +ndigits+ decimal digits (default: 0).
*
* When the precision is negative, the returned value is an integer
* with at least <code>ndigits.abs</code> trailing zeros.
*
* Returns a floating-point number when +ndigits+ is positive,
* otherwise returns an integer.
*
* 1.2.ceil #=> 2
* 2.0.ceil #=> 2
* (-1.2).ceil #=> -1
* (-2.0).ceil #=> -2
*
* 1.234567.ceil(2) #=> 1.24
* 1.234567.ceil(3) #=> 1.235
* 1.234567.ceil(4) #=> 1.2346
* 1.234567.ceil(5) #=> 1.23457
*
* 34567.89.ceil(-5) #=> 100000
* 34567.89.ceil(-4) #=> 40000
* 34567.89.ceil(-3) #=> 35000
* 34567.89.ceil(-2) #=> 34600
* 34567.89.ceil(-1) #=> 34570
* 34567.89.ceil(0) #=> 34568
* 34567.89.ceil(1) #=> 34567.9
* 34567.89.ceil(2) #=> 34567.89
* 34567.89.ceil(3) #=> 34567.89
*
* Note that the limited precision of floating-point arithmetic
* might lead to surprising results:
*
* (2.1 / 0.7).ceil #=> 4 (!)
*/
static mrb_value
flo_ceil(mrb_state *mrb, mrb_value num)
{
return flo_rounding(mrb, num, ceil);
}
/* 15.2.9.3.12 */
/*
* call-seq:
* 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 mrb_value
flo_round(mrb_state *mrb, mrb_value num)
{
double number, f;
mrb_int ndigits = 0;
mrb_int i;
mrb_get_args(mrb, "|i", &ndigits);
number = mrb_float(num);
if (0 < ndigits && (isinf(number) || isnan(number))) {
return num;
}
mrb_check_num_exact(mrb, number);
f = 1.0;
if (ndigits < -DBL_DIG-2) return mrb_fixnum_value(0);
i = ndigits >= 0 ? ndigits : -ndigits;
if (ndigits > DBL_DIG+2) return num;
while (--i >= 0)
f = f*10.0;
if (isinf(f)) {
if (ndigits < 0) number = 0;
}
else {
double d;
if (ndigits < 0) number /= f;
else number *= f;
/* home-made inline implementation of round(3) */
if (number > 0.0) {
d = floor(number);
number = d + (number - d >= 0.5);
}
else if (number < 0.0) {
d = ceil(number);
number = d - (d - number >= 0.5);
}
if (ndigits < 0) number *= f;
else number /= f;
}
if (ndigits > 0) {
if (!isfinite(number)) return num;
return mrb_float_value(mrb, number);
}
if (!FIXABLE_FLOAT(number))
return mrb_float_value(mrb, number);
return mrb_int_value(mrb, (mrb_int)number);
}
/* 15.2.9.3.14 */
static mrb_value
flo_to_i(mrb_state *mrb, mrb_value num)
{
mrb_float f = mrb_float(num);
mrb_check_num_exact(mrb, f);
if (!FIXABLE_FLOAT(f)) {
#ifdef MRB_USE_BIGINT
return mrb_bint_new_float(mrb, f);
#else
mrb_int_overflow(mrb, "to_f");
#endif
}
if (f > 0.0) f = floor(f);
if (f < 0.0) f = ceil(f);
return mrb_int_value(mrb, (mrb_int)f);
}
/* 15.2.9.3.15 */
/*
* call-seq:
* flt.to_i -> integer
* flt.truncate -> integer
*
* Returns <i>flt</i> truncated to an <code>Integer</code>.
*/
static mrb_value
flo_truncate(mrb_state *mrb, mrb_value num)
{
if (signbit(mrb_float(num))) return flo_ceil(mrb, num);
return flo_floor(mrb, num);
}
static mrb_value
flo_nan_p(mrb_state *mrb, mrb_value num)
{
return mrb_bool_value(isnan(mrb_float(num)));
}
static mrb_value
flo_abs(mrb_state *mrb, mrb_value num)
{
mrb_float f = mrb_float(num);
if (signbit(f)) return mrb_float_value(mrb, -f);
return num;
}
#endif
/*
* Document-class: Integer
*
* <code>Integer</code> is hold whole numbers.
*
*/
/* 15.2.9.3.24 */
/*
* Document-method: Integer#to_i
* Document-method: Integer#to_int
*
* call-seq:
* int.to_i -> integer
* int.to_int -> integer
*
* As <i>int</i> is already an <code>Integer</code>, all these
* methods simply return the receiver.
*/
mrb_value
mrb_int_mul(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_int a;
a = mrb_integer(x);
if (mrb_integer_p(y)) {
mrb_int b, c;
if (a == 0) return x;
if (a == 1) return y;
b = mrb_integer(y);
if (b == 0) return y;
if (b == 1) return x;
if (mrb_int_mul_overflow(a, b, &c)) {
#ifdef MRB_USE_BIGINT
x = mrb_bint_new_int(mrb, a);
return mrb_bint_mul(mrb, x, y);
#else
mrb_int_overflow(mrb, "multiplication");
#endif
}
return mrb_int_value(mrb, c);
}
switch (mrb_type(y)) {
#ifdef MRB_USE_BIGINT
case MRB_TT_BIGINT:
if (a == 0) return x;
if (a == 1) return y;
return mrb_bint_mul(mrb, y, x);
#endif
#ifdef MRB_USE_RATIONAL
case MRB_TT_RATIONAL:
if (a == 0) return x;
if (a == 1) return y;
return mrb_rational_mul(mrb, y, x);
#endif
#ifdef MRB_USE_COMPLEX
case MRB_TT_COMPLEX:
if (a == 0) return x;
if (a == 1) return y;
return mrb_complex_mul(mrb, y, x);
#endif
#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
return mrb_float_value(mrb, (mrb_float)a * mrb_as_float(mrb, y));
#endif
default:
mrb_int_noconv(mrb, y);
}
}
/* 15.2.8.3.5 */
/*
* call-seq:
* int * 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
* result.
*/
static mrb_value
int_mul(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
return mrb_bint_mul(mrb, x, y);
}
#endif
return mrb_int_mul(mrb, x, y);
}
static void
intdivmod(mrb_state *mrb, mrb_int x, mrb_int y, mrb_int *divp, mrb_int *modp)
{
if (y == 0) {
mrb_int_zerodiv(mrb);
}
else if (x == MRB_INT_MIN && y == -1) {
mrb_int_overflow(mrb, "division");
}
else {
mrb_int div = x / y;
mrb_int mod = x - div * y;
if ((x ^ y) < 0 && x != div * y) {
mod += y;
div -= 1;
}
if (divp) *divp = div;
if (modp) *modp = mod;
}
}
/* 15.2.8.3.7 */
/*
* call-seq:
* int % num -> num
*
* Returns <code>int</code> modulo <code>other</code>.
* See <code>numeric.divmod</code> for more information.
*/
static mrb_value
int_mod(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
mrb_int a, b;
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
return mrb_bint_mod(mrb, x, y);
}
#endif
a = mrb_integer(x);
if (a == 0) return x;
if (mrb_integer_p(y)) {
b = mrb_integer(y);
if (b == 0) mrb_int_zerodiv(mrb);
if (a == MRB_INT_MIN && b == -1) return mrb_fixnum_value(0);
mrb_int mod = a % b;
if ((a < 0) != (b < 0) && mod != 0) {
mod += b;
}
return mrb_int_value(mrb, mod);
}
#ifdef MRB_NO_FLOAT
mrb_raise(mrb, E_TYPE_ERROR, "non integer modulo");
#else
mrb_float mod;
flodivmod(mrb, (mrb_float)a, mrb_as_float(mrb, y), NULL, &mod);
return mrb_float_value(mrb, mod);
#endif
}
#ifndef MRB_NO_FLOAT
static mrb_value flo_divmod(mrb_state *mrb, mrb_value x);
#endif
/*
* call-seq:
* int.divmod(numeric) -> array
*
* See <code>Numeric#divmod</code>.
*/
static mrb_value
int_divmod(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
#ifndef MRB_NO_FLOAT
if (mrb_float_p(y)) {
mrb_float f = mrb_bint_as_float(mrb, x);
return flo_divmod(mrb, mrb_float_value(mrb, f));
}
#endif
return mrb_bint_divmod(mrb, x, y);
}
#endif
if (mrb_integer_p(y)) {
mrb_int div, mod;
intdivmod(mrb, mrb_integer(x), mrb_integer(y), &div, &mod);
return mrb_assoc_new(mrb, mrb_int_value(mrb, div), mrb_int_value(mrb, mod));
}
#ifdef MRB_NO_FLOAT
mrb_raise(mrb, E_TYPE_ERROR, "non integer divmod");
#else
return flo_divmod(mrb, x);
#endif
}
#ifndef MRB_NO_FLOAT
static mrb_value
flo_divmod(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
mrb_float div, mod;
mrb_value a, b;
flodivmod(mrb, mrb_float(x), mrb_as_float(mrb, y), &div, &mod);
if (!FIXABLE_FLOAT(div))
a = mrb_float_value(mrb, div);
else
a = mrb_int_value(mrb, (mrb_int)div);
b = mrb_float_value(mrb, mod);
return mrb_assoc_new(mrb, a, b);
}
#endif
/* 15.2.8.3.2 */
/*
* call-seq:
* int == other -> true or false
*
* Return <code>true</code> if <code>int</code> equals <code>other</code>
* numerically.
*
* 1 == 2 #=> false
* 1 == 1.0 #=> true
*/
static mrb_value
int_equal(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
switch (mrb_type(y)) {
case MRB_TT_INTEGER:
return mrb_bool_value(mrb_integer(x) == mrb_integer(y));
#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
return mrb_bool_value((mrb_float)mrb_integer(x) == mrb_float(y));
#endif
#ifdef MRB_USE_BIGINT
case MRB_TT_BIGINT:
return mrb_bool_value(mrb_bint_cmp(mrb, y, x) == 0);
#endif
#ifdef MRB_USE_RATIONAL
case MRB_TT_RATIONAL:
return mrb_bool_value(mrb_equal(mrb, y, x));
#endif
#ifdef MRB_USE_COMPLEX
case MRB_TT_COMPLEX:
return mrb_bool_value(mrb_equal(mrb, y, x));
#endif
default:
return mrb_false_value();
}
}
/* 15.2.8.3.8 */
/*
* call-seq:
* ~int -> integer
*
* One's complement: returns a number where each bit is flipped.
* ex.0---00001 (1)-> 1---11110 (-2)
* ex.0---00010 (2)-> 1---11101 (-3)
* ex.0---00100 (4)-> 1---11011 (-5)
*/
static mrb_value
int_rev(mrb_state *mrb, mrb_value num)
{
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(num)) {
return mrb_bint_rev(mrb, num);
}
#endif
mrb_int val = mrb_integer(num);
return mrb_int_value(mrb, ~val);
}
#define bit_op(x,y,op1,op2) do {\
return mrb_int_value(mrb, (mrb_integer(x) op2 mrb_integer(y)));\
} while(0)
/* 15.2.8.3.9 */
/*
* call-seq:
* int & integer -> integer_result
*
* Bitwise AND.
*/
static mrb_value
int_and(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
return mrb_bint_and(mrb, x, y);
}
if (mrb_bigint_p(y)) {
return mrb_bint_and(mrb, mrb_as_bint(mrb, x), y);
}
#endif
bit_op(x, y, and, &);
}
/* 15.2.8.3.10 */
/*
* call-seq:
* int | integer -> integer_result
*
* Bitwise OR.
*/
static mrb_value
int_or(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
return mrb_bint_or(mrb, x, y);
}
if (mrb_bigint_p(y)) {
return mrb_bint_or(mrb, mrb_as_bint(mrb, x), y);
}
#endif
bit_op(x, y, or, |);
}
/* 15.2.8.3.11 */
/*
* call-seq:
* int ^ integer -> integer_result
*
* Bitwise EXCLUSIVE OR.
*/
static mrb_value
int_xor(mrb_state *mrb, mrb_value x)
{
mrb_value y = mrb_get_arg1(mrb);
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
return mrb_bint_xor(mrb, x, y);
}
if (mrb_bigint_p(y)) {
return mrb_bint_xor(mrb, mrb_as_bint(mrb, x), y);
}
#endif
bit_op(x, y, xor, ^);
}
#define NUMERIC_SHIFT_WIDTH_MAX (MRB_INT_BIT-1)
mrb_bool
mrb_num_shift(mrb_state *mrb, mrb_int val, mrb_int width, mrb_int *num)
{
if (width < 0) { /* rshift */
if (width == MRB_INT_MIN || -width >= NUMERIC_SHIFT_WIDTH_MAX) {
if (val < 0) {
*num = -1;
}
else {
*num = 0;
}
}
else {
*num = val >> -width;
}
}
else if (val > 0) {
if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
(val > (MRB_INT_MAX >> width))) {
return FALSE;
}
*num = val << width;
}
else {
if ((width > NUMERIC_SHIFT_WIDTH_MAX) ||
(val < (MRB_INT_MIN >> width))) {
return FALSE;
}
if (width == NUMERIC_SHIFT_WIDTH_MAX)
*num = MRB_INT_MIN;
else
*num = val * ((mrb_int)1 << width);
}
return TRUE;
}
/* 15.2.8.3.12 */
/*
* call-seq:
* int << count -> integer or float
*
* Shifts _int_ left _count_ positions (right if _count_ is negative).
*/
static mrb_value
int_lshift(mrb_state *mrb, mrb_value x)
{
mrb_int width, val;
width = mrb_as_int(mrb, mrb_get_arg1(mrb));
if (width == 0) {
return x;
}
if (width == MRB_INT_MIN) mrb_int_overflow(mrb, "bit shift");
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
return mrb_bint_lshift(mrb, x, width);
}
#endif
val = mrb_integer(x);
if (val == 0) return x;
if (!mrb_num_shift(mrb, val, width, &val)) {
#ifdef MRB_USE_BIGINT
return mrb_bint_lshift(mrb, mrb_bint_new_int(mrb, val), width);
#else
mrb_int_overflow(mrb, "bit shift");
#endif
}
return mrb_int_value(mrb, val);
}
/* 15.2.8.3.13 */
/*
* call-seq:
* int >> count -> integer or float
*
* Shifts _int_ right _count_ positions (left if _count_ is negative).
*/
static mrb_value
int_rshift(mrb_state *mrb, mrb_value x)
{
mrb_int width, val;
width = mrb_as_int(mrb, mrb_get_arg1(mrb));
if (width == 0) {
return x;
}
if (width == MRB_INT_MIN) mrb_int_overflow(mrb, "bit shift");
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
return mrb_bint_rshift(mrb, x, width);
}
#endif
val = mrb_integer(x);
if (val == 0) return x;
if (!mrb_num_shift(mrb, val, -width, &val)) {
#ifdef MRB_USE_BIGINT
return mrb_bint_rshift(mrb, mrb_bint_new_int(mrb, val), width);
#else
mrb_int_overflow(mrb, "bit shift");
#endif
}
return mrb_int_value(mrb, val);
}
static mrb_value
prepare_int_rounding(mrb_state *mrb, mrb_value x)
{
mrb_int nd = 0;
size_t bytes;
mrb_get_args(mrb, "|i", &nd);
if (nd >= 0) {
return mrb_nil_value();
}
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
bytes = mrb_bint_memsize(x);
}
else
#endif
bytes = sizeof(mrb_int);
if (-0.415241 * nd - 0.125 > bytes) {
return mrb_undef_value();
}
return mrb_int_pow(mrb, mrb_fixnum_value(10), mrb_fixnum_value(-nd));
}
/* 15.2.8.3.14 Integer#ceil */
/*
* call-seq:
* int.ceil -> int
* int.ceil(ndigits) -> int
*
* Returns self.
*
* When the precision (ndigits) is negative, the returned value is an integer
* with at least <code>ndigits.abs</code> trailing zeros.
*/
static mrb_value
int_ceil(mrb_state *mrb, mrb_value x)
{
mrb_value f = prepare_int_rounding(mrb, x);
if (mrb_undef_p(f)) return mrb_fixnum_value(0);
if (mrb_nil_p(f)) return x;
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
x = mrb_bint_add_d(mrb, x, f);
return mrb_bint_sub(mrb, x, mrb_bint_mod(mrb, x, f));
}
#endif
mrb_int a = mrb_integer(x);
mrb_int b = mrb_integer(f);
mrb_int c = a % b;
int neg = a < 0;
a -= c;
if (!neg) {
if (mrb_int_add_overflow(a, b, &c)) {
#ifdef MRB_USE_BIGINT
x = mrb_bint_new_int(mrb, a);
return mrb_bint_add(mrb, x, f);
#else
mrb_int_overflow(mrb, "ceil");
#endif
}
a = c;
}
return mrb_int_value(mrb, a);
}
/* 15.2.8.3.17 Integer#floor */
/*
* call-seq:
* int.floor -> int
* int.floor(ndigits) -> int
*
* Returns self.
*
* When the precision (ndigits) is negative, the returned value is an integer
* with at least <code>ndigits.abs</code> trailing zeros.
*/
static mrb_value
int_floor(mrb_state *mrb, mrb_value x)
{
mrb_value f = prepare_int_rounding(mrb, x);
if (mrb_undef_p(f)) return mrb_fixnum_value(0);
if (mrb_nil_p(f)) return x;
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
return mrb_bint_sub(mrb, x, mrb_bint_mod(mrb, x, f));
}
#endif
mrb_int a = mrb_integer(x);
mrb_int b = mrb_integer(f);
mrb_int c = a % b;
int neg = a < 0;
a -= c;
if (neg) {
if (mrb_int_sub_overflow(a, b, &c)) {
#ifdef MRB_USE_BIGINT
x = mrb_bint_new_int(mrb, a);
return mrb_bint_sub(mrb, x, f);
#else
mrb_int_overflow(mrb, "floor");
#endif
}
a = c;
}
return mrb_int_value(mrb, a);
}
/* 15.2.8.3.20 Integer#round */
/*
* call-seq:
* int.round -> int
* int.round(ndigits) -> int
*
* Returns self.
*
* When the precision (ndigits) is negative, the returned value is an integer
* with at least <code>ndigits.abs</code> trailing zeros.
*/
static mrb_value
int_round(mrb_state *mrb, mrb_value x)
{
mrb_value f = prepare_int_rounding(mrb, x);
if (mrb_undef_p(f)) return mrb_fixnum_value(0);
if (mrb_nil_p(f)) return x;
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
mrb_value r = mrb_bint_mod(mrb, x, f);
mrb_value n = mrb_bint_sub(mrb, x, r);
mrb_value h = mrb_bigint_p(f) ? mrb_bint_rshift(mrb, f, 1) : mrb_int_value(mrb, mrb_integer(f)>>1);
mrb_int cmp = mrb_bigint_p(r) ? mrb_bint_cmp(mrb, r, h) : (mrb_bigint_p(h) ? -mrb_bint_cmp(mrb, h, r) : (mrb_integer(r)-mrb_integer(h)));
if ((cmp > 0) || (cmp == 0 && mrb_bint_cmp(mrb, x, mrb_fixnum_value(0)) > 0)) {
n = mrb_as_bint(mrb, n);
n = mrb_bint_add(mrb, n, f);
}
return n;
}
#endif
mrb_int a = mrb_integer(x);
mrb_int b = mrb_integer(f);
mrb_int c = a % b;
a -= c;
if (c < 0) {
c = -c;
if (b/2 < c) {
if (mrb_int_sub_overflow(a, b, &c)) {
#ifdef MRB_USE_BIGINT
x = mrb_bint_new_int(mrb, a);
return mrb_bint_sub(mrb, x, f);
#else
mrb_int_overflow(mrb, "round");
#endif
}
}
a = c;
}
else {
if (b/2 < c) {
if (mrb_int_add_overflow(a, b, &c)) {
#ifdef MRB_USE_BIGINT
x = mrb_bint_new_int(mrb, a);
return mrb_bint_add(mrb, x, f);
#else
mrb_int_overflow(mrb, "round");
#endif
}
}
a = c;
}
return mrb_int_value(mrb, a);
}
/* 15.2.8.3.26 Integer#truncate */
/*
* call-seq:
* int.truncate -> int
* int.truncate(ndigits) -> int
*
* Returns self.
*
* When the precision (ndigits) is negative, the returned value is an integer
* with at least <code>ndigits.abs</code> trailing zeros.
*/
static mrb_value
int_truncate(mrb_state *mrb, mrb_value x)
{
mrb_value f = prepare_int_rounding(mrb, x);
if (mrb_undef_p(f)) return mrb_fixnum_value(0);
if (mrb_nil_p(f)) return x;
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
mrb_value m = mrb_bint_mod(mrb, x, f);
x = mrb_bint_sub_d(mrb, x, m);
if (mrb_bint_cmp(mrb, x, mrb_fixnum_value(0)) < 0) {
return mrb_bint_add(mrb, x, f);
}
return x;
}
#endif
mrb_int a = mrb_integer(x);
mrb_int b = mrb_integer(f);
return mrb_int_value(mrb, a - (a % b));
}
/* 15.2.8.3.23 */
/*
* call-seq:
* int.to_f -> float
*
* Converts <i>int</i> to a <code>Float</code>.
*
*/
#ifndef MRB_NO_FLOAT
static mrb_value
int_to_f(mrb_state *mrb, mrb_value num)
{
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(num)) {
return mrb_float_value(mrb, mrb_bint_as_float(mrb, num));
}
#endif
return mrb_float_value(mrb, (mrb_float)mrb_integer(num));
}
MRB_API mrb_value
mrb_float_to_integer(mrb_state *mrb, mrb_value x)
{
if (!mrb_float_p(x)) {
mrb_raise(mrb, E_TYPE_ERROR, "non float value");
}
mrb_float f = mrb_float(x);
if (isinf(f) || isnan(f)) {
mrb_raisef(mrb, E_RANGE_ERROR, "float %f out of range", f);
}
return flo_to_i(mrb, x);
}
#endif
mrb_value
mrb_int_add(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_int a;
a = mrb_integer(x);
if (mrb_integer_p(y)) {
mrb_int b, c;
if (a == 0) return y;
b = mrb_integer(y);
if (b == 0) return x;
if (mrb_int_add_overflow(a, b, &c)) {
#ifdef MRB_USE_BIGINT
x = mrb_bint_new_int(mrb, a);
return mrb_bint_add(mrb, x, y);
#else
mrb_int_overflow(mrb, "addition");
#endif
}
return mrb_int_value(mrb, c);
}
switch (mrb_type(y)) {
#ifdef MRB_USE_BIGINT
case MRB_TT_BIGINT:
return mrb_bint_add(mrb, y, x);
#endif
#ifdef MRB_USE_RATIONAL
case MRB_TT_RATIONAL:
return mrb_rational_add(mrb, y, x);
#endif
#ifdef MRB_USE_COMPLEX
case MRB_TT_COMPLEX:
return mrb_complex_add(mrb, y, x);
#endif
default:
#ifdef MRB_NO_FLOAT
mrb_raise(mrb, E_TYPE_ERROR, "non integer addition");
#else
return mrb_float_value(mrb, (mrb_float)a + mrb_as_float(mrb, y));
#endif
}
}
/* 15.2.8.3.3 */
/*
* call-seq:
* int + 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
* result.
*/
static mrb_value
int_add(mrb_state *mrb, mrb_value self)
{
mrb_value other = mrb_get_arg1(mrb);
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(self)) {
return mrb_bint_add(mrb, self, other);
}
#endif
return mrb_int_add(mrb, self, other);
}
mrb_value
mrb_int_sub(mrb_state *mrb, mrb_value x, mrb_value y)
{
mrb_int a;
a = mrb_integer(x);
if (mrb_integer_p(y)) {
mrb_int b, c;
b = mrb_integer(y);
if (mrb_int_sub_overflow(a, b, &c)) {
#ifdef MRB_USE_BIGINT
x = mrb_bint_new_int(mrb, a);
return mrb_bint_sub(mrb, x, y);
#else
mrb_int_overflow(mrb, "subtraction");
#endif
}
return mrb_int_value(mrb, c);
}
switch (mrb_type(y)) {
#ifdef MRB_USE_BIGINT
case MRB_TT_BIGINT:
return mrb_bint_sub(mrb, mrb_bint_new_int(mrb, a), y);
#endif
#ifdef MRB_USE_RATIONAL
case MRB_TT_RATIONAL:
return mrb_rational_sub(mrb, mrb_rational_new(mrb, a, 1), y);
#endif
#ifdef MRB_USE_COMPLEX
case MRB_TT_COMPLEX:
return mrb_complex_sub(mrb, mrb_complex_new(mrb, (mrb_float)a, 0), y);
#endif
default:
#ifdef MRB_NO_FLOAT
mrb_raise(mrb, E_TYPE_ERROR, "non integer subtraction");
#else
return mrb_float_value(mrb, (mrb_float)a - mrb_as_float(mrb, y));
#endif
}
}
/* 15.2.8.3.4 */
/*
* call-seq:
* int - numeric -> numeric
*
* Performs subtraction: the class of the resulting object depends on
* the class of <code>numeric</code> and on the magnitude of the
* result.
*/
static mrb_value
int_sub(mrb_state *mrb, mrb_value self)
{
mrb_value other = mrb_get_arg1(mrb);
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(self)) {
return mrb_bint_sub(mrb, self, other);
}
#endif
return mrb_int_sub(mrb, self, other);
}
MRB_API char*
mrb_int_to_cstr(char *buf, size_t len, mrb_int n, mrb_int base)
{
char *bufend = buf + len;
char *b = bufend-1;
if (base < 2 || 36 < base) return NULL;
if (len < 2) return NULL;
if (n == 0) {
buf[0] = '0';
buf[1] = '\0';
return buf;
}
*b = '\0';
if (n < 0) {
do {
if (b-- == buf) return NULL;
*b = mrb_digitmap[-(n % base)];
} while (n /= base);
if (b-- == buf) return NULL;
*b = '-';
}
else {
do {
if (b-- == buf) return NULL;
*b = mrb_digitmap[(int)(n % base)];
} while (n /= base);
}
return b;
}
MRB_API mrb_value
mrb_integer_to_str(mrb_state *mrb, mrb_value x, mrb_int base)
{
char buf[MRB_INT_BIT+1];
if (base < 2 || 36 < base) {
mrb_raisef(mrb, E_ARGUMENT_ERROR, "invalid radix %i", base);
}
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(x)) {
return mrb_bint_to_s(mrb, x, base);
}
#endif
mrb_int val = mrb_integer(x);
const char *p = mrb_int_to_cstr(buf, sizeof(buf), val, base);
mrb_assert(p != NULL);
mrb_value str = mrb_str_new_cstr(mrb, p);
RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
return str;
}
/* 15.2.8.3.25 */
/*
* call-seq:
* int.to_s(base=10) -> string
*
* Returns a string containing the representation of <i>int</i> radix
* <i>base</i> (between 2 and 36).
*
* 12345.to_s #=> "12345"
* 12345.to_s(2) #=> "11000000111001"
* 12345.to_s(8) #=> "30071"
* 12345.to_s(10) #=> "12345"
* 12345.to_s(16) #=> "3039"
* 12345.to_s(36) #=> "9ix"
*
*/
static mrb_value
int_to_s(mrb_state *mrb, mrb_value self)
{
mrb_int base;
if (mrb_get_argc(mrb) > 0) {
base = mrb_integer(mrb_get_arg1(mrb));
}
else {
base = 10;
}
return mrb_integer_to_str(mrb, self, base);
}
/* compare two numbers: (1:0:-1; -2 for error) */
static mrb_int
cmpnum(mrb_state *mrb, mrb_value v1, mrb_value v2)
{
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(v1)) {
return mrb_bint_cmp(mrb, v1, v2);
}
if (mrb_bigint_p(v2)) {
return mrb_bint_cmp(mrb, mrb_bint_new_int(mrb, mrb_integer(v1)), v2);
}
#endif
#ifdef MRB_NO_FLOAT
mrb_int x, y;
#else
mrb_float x, y;
#endif
#ifdef MRB_NO_FLOAT
x = mrb_integer(v1);
#else
x = mrb_as_float(mrb, v1);
#endif
switch (mrb_type(v2)) {
case MRB_TT_INTEGER:
#ifdef MRB_NO_FLOAT
y = mrb_integer(v2);
#else
y = (mrb_float)mrb_integer(v2);
#endif
break;
#ifndef MRB_NO_FLOAT
case MRB_TT_FLOAT:
y = mrb_float(v2);
break;
#ifdef MRB_USE_RATIONAL
case MRB_TT_RATIONAL:
y = mrb_as_float(mrb, v2);
break;
#endif
#endif
default:
return -2;
}
if (x > y)
return 1;
else {
if (x < y)
return -1;
return 0;
}
}
static mrb_value
int_hash(mrb_state *mrb, mrb_value self)
{
#ifdef MRB_USE_BIGINT
if (mrb_bigint_p(self)) {
return mrb_bint_hash(mrb, self);
}
#endif
mrb_int n = mrb_integer(self);
return mrb_int_value(mrb, mrb_byte_hash((uint8_t*)&n, sizeof(n)));
}
/* 15.2.8.3.1 */
/* 15.2.9.3.1 */
/*
* call-seq:
* self.f <=> other.f => -1, 0, +1, or nil
* < => -1
* = => 0
* > => +1
* Comparison---Returns -1, 0, or +1 depending on whether <i>int</i> is
* less than, equal to, or greater than <i>numeric</i>. This is the
* basis for the tests in <code>Comparable</code>. When the operands are
* not comparable, it returns nil instead of raising an exception.
*/
static mrb_value
num_cmp(mrb_state *mrb, mrb_value self)
{
mrb_value other = mrb_get_arg1(mrb);
mrb_int n;
n = cmpnum(mrb, self, other);
if (n == -2) return mrb_nil_value();
return mrb_fixnum_value(n);
}
static mrb_noreturn void
cmperr(mrb_state *mrb, mrb_value v1, mrb_value v2)
{
mrb_raisef(mrb, E_ARGUMENT_ERROR, "comparison of %t with %t failed", v1, v2);
}
static mrb_value
num_lt(mrb_state *mrb, mrb_value self)
{
mrb_value other = mrb_get_arg1(mrb);
mrb_int n;
n = cmpnum(mrb, self, other);
if (n == -2) cmperr(mrb, self, other);
if (n < 0) return mrb_true_value();
return mrb_false_value();
}
static mrb_value
num_le(mrb_state *mrb, mrb_value self)
{
mrb_value other = mrb_get_arg1(mrb);
mrb_int n;
n = cmpnum(mrb, self, other);
if (n == -2) cmperr(mrb, self, other);
if (n <= 0) return mrb_true_value();
return mrb_false_value();
}
static mrb_value
num_gt(mrb_state *mrb, mrb_value self)
{
mrb_value other = mrb_get_arg1(mrb);
mrb_int n;
n = cmpnum(mrb, self, other);
if (n == -2) cmperr(mrb, self, other);
if (n > 0) return mrb_true_value();
return mrb_false_value();
}
static mrb_value
num_ge(mrb_state *mrb, mrb_value self)
{
mrb_value other = mrb_get_arg1(mrb);
mrb_int n;
n = cmpnum(mrb, self, other);
if (n == -2) cmperr(mrb, self, other);
if (n >= 0) return mrb_true_value();
return mrb_false_value();
}
MRB_API mrb_int
mrb_cmp(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
{
mrb_value v;
switch (mrb_type(obj1)) {
case MRB_TT_INTEGER:
case MRB_TT_FLOAT:
case MRB_TT_BIGINT:
return cmpnum(mrb, obj1, obj2);
case MRB_TT_STRING:
if (!mrb_string_p(obj2))
return -2;
return mrb_str_cmp(mrb, obj1, obj2);
default:
if (!mrb_respond_to(mrb, obj1, MRB_OPSYM(cmp))) return -2;
v = mrb_funcall_argv(mrb, obj1, MRB_OPSYM(cmp), 1, &obj2);
if (mrb_nil_p(v) || !mrb_integer_p(v))
return -2;
return mrb_integer(v);
}
}
static mrb_value
num_finite_p(mrb_state *mrb, mrb_value self)
{
return mrb_true_value();
}
static mrb_value
num_infinite_p(mrb_state *mrb, mrb_value self)
{
return mrb_false_value();
}
#ifndef MRB_NO_FLOAT
static mrb_value
flo_hash(mrb_state *mrb, mrb_value flo)
{
mrb_float f = mrb_float(flo);
/* normalize -0.0 to 0.0 */
if (f == 0) f = 0.0;
return mrb_int_value(mrb, (mrb_int)mrb_byte_hash((uint8_t*)&f, sizeof(f)));
}
#endif
/* ------------------------------------------------------------------------*/
void
mrb_init_numeric(mrb_state *mrb)
{
struct RClass *numeric, *integer;
#ifndef MRB_NO_FLOAT
struct RClass *fl;
#endif
/* Numeric Class */
numeric = mrb_define_class_id(mrb, MRB_SYM(Numeric), mrb->object_class); /* 15.2.7 */
mrb_define_method_id(mrb, numeric, MRB_SYM_Q(finite), num_finite_p, MRB_ARGS_NONE());
mrb_define_method_id(mrb, numeric, MRB_SYM_Q(infinite),num_infinite_p, MRB_ARGS_NONE());
mrb_define_method_id(mrb, numeric, MRB_SYM_Q(eql), num_eql, MRB_ARGS_REQ(1)); /* 15.2.8.3.16 */
/* Integer Class */
mrb->integer_class = integer = mrb_define_class_id(mrb, MRB_SYM(Integer), numeric); /* 15.2.8 */
MRB_SET_INSTANCE_TT(integer, MRB_TT_INTEGER);
MRB_UNDEF_ALLOCATOR(integer);
mrb_undef_class_method_id(mrb, integer, MRB_SYM(new));
mrb_define_method_id(mrb, integer, MRB_OPSYM(pow), int_pow, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, integer, MRB_OPSYM(cmp), num_cmp, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */
mrb_define_method_id(mrb, integer, MRB_OPSYM(lt), num_lt, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, integer, MRB_OPSYM(le), num_le, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, integer, MRB_OPSYM(gt), num_gt, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, integer, MRB_OPSYM(ge), num_ge, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, integer, MRB_SYM(to_i), mrb_obj_itself, MRB_ARGS_NONE()); /* 15.2.8.3.24 */
mrb_define_method_id(mrb, integer, MRB_SYM(to_int), mrb_obj_itself, MRB_ARGS_NONE());
mrb_define_method_id(mrb, integer, MRB_OPSYM(add), int_add, MRB_ARGS_REQ(1)); /* 15.2.8.3.1 */
mrb_define_method_id(mrb, integer, MRB_OPSYM(sub), int_sub, MRB_ARGS_REQ(1)); /* 15.2.8.3.2 */
mrb_define_method_id(mrb, integer, MRB_OPSYM(mul), int_mul, MRB_ARGS_REQ(1)); /* 15.2.8.3.3 */
mrb_define_method_id(mrb, integer, MRB_OPSYM(mod), int_mod, MRB_ARGS_REQ(1)); /* 15.2.8.3.5 */
mrb_define_method_id(mrb, integer, MRB_OPSYM(div), int_div, MRB_ARGS_REQ(1)); /* 15.2.8.3.6 */
mrb_define_method_id(mrb, integer, MRB_SYM(quo), int_quo, MRB_ARGS_REQ(1)); /* 15.2.7.4.5(x) */
mrb_define_method_id(mrb, integer, MRB_SYM(div), int_idiv, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, integer, MRB_OPSYM(eq), int_equal, MRB_ARGS_REQ(1)); /* 15.2.8.3.7 */
mrb_define_method_id(mrb, integer, MRB_OPSYM(neg), int_rev, MRB_ARGS_NONE()); /* 15.2.8.3.8 */
mrb_define_method_id(mrb, integer, MRB_OPSYM(and), int_and, MRB_ARGS_REQ(1)); /* 15.2.8.3.9 */
mrb_define_method_id(mrb, integer, MRB_OPSYM(or), int_or, MRB_ARGS_REQ(1)); /* 15.2.8.3.10 */
mrb_define_method_id(mrb, integer, MRB_OPSYM(xor), int_xor, MRB_ARGS_REQ(1)); /* 15.2.8.3.11 */
mrb_define_method_id(mrb, integer, MRB_OPSYM(lshift), int_lshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.12 */
mrb_define_method_id(mrb, integer, MRB_OPSYM(rshift), int_rshift, MRB_ARGS_REQ(1)); /* 15.2.8.3.13 */
mrb_define_method_id(mrb, integer, MRB_SYM(ceil), int_ceil, MRB_ARGS_OPT(1)); /* 15.2.8.3.14 */
mrb_define_method_id(mrb, integer, MRB_SYM(floor), int_floor, MRB_ARGS_OPT(1)); /* 15.2.8.3.17 */
mrb_define_method_id(mrb, integer, MRB_SYM(round), int_round, MRB_ARGS_OPT(1)); /* 15.2.8.3.20 */
mrb_define_method_id(mrb, integer, MRB_SYM(truncate), int_truncate, MRB_ARGS_OPT(1)); /* 15.2.8.3.26 */
mrb_define_method_id(mrb, integer, MRB_SYM(hash), int_hash, MRB_ARGS_NONE()); /* 15.2.8.3.18 */
#ifndef MRB_NO_FLOAT
mrb_define_method_id(mrb, integer, MRB_SYM(to_f), int_to_f, MRB_ARGS_NONE()); /* 15.2.8.3.23 */
#endif
mrb_define_method_id(mrb, integer, MRB_SYM(to_s), int_to_s, MRB_ARGS_OPT(1)); /* 15.2.8.3.25 */
mrb_define_method_id(mrb, integer, MRB_SYM(inspect), int_to_s, MRB_ARGS_OPT(1));
mrb_define_method_id(mrb, integer, MRB_SYM(divmod), int_divmod, MRB_ARGS_REQ(1)); /* 15.2.8.3.30(x) */
mrb_define_method_id(mrb, integer, MRB_SYM(__coerce_step_counter), coerce_step_counter, MRB_ARGS_REQ(2));
/* Fixnum Class for compatibility */
mrb_define_const_id(mrb, mrb->object_class, MRB_SYM(Fixnum), mrb_obj_value(integer));
#ifndef MRB_NO_FLOAT
/* Float Class */
mrb->float_class = fl = mrb_define_class_id(mrb, MRB_SYM(Float), numeric); /* 15.2.9 */
MRB_SET_INSTANCE_TT(fl, MRB_TT_FLOAT);
MRB_UNDEF_ALLOCATOR(fl);
mrb_undef_class_method(mrb, fl, "new");
mrb_define_method_id(mrb, fl, MRB_OPSYM(pow), flo_pow, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, fl, MRB_OPSYM(div), flo_div, MRB_ARGS_REQ(1)); /* 15.2.9.3.6 */
mrb_define_method_id(mrb, fl, MRB_SYM(quo), flo_div, MRB_ARGS_REQ(1)); /* 15.2.7.4.5(x) */
mrb_define_method_id(mrb, fl, MRB_SYM(div), flo_idiv, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, fl, MRB_OPSYM(add), flo_add, MRB_ARGS_REQ(1)); /* 15.2.9.3.3 */
mrb_define_method_id(mrb, fl, MRB_OPSYM(sub), flo_sub, MRB_ARGS_REQ(1)); /* 15.2.9.3.4 */
mrb_define_method_id(mrb, fl, MRB_OPSYM(mul), flo_mul, MRB_ARGS_REQ(1)); /* 15.2.9.3.5 */
mrb_define_method_id(mrb, fl, MRB_OPSYM(mod), flo_mod, MRB_ARGS_REQ(1)); /* 15.2.9.3.7 */
mrb_define_method_id(mrb, fl, MRB_OPSYM(cmp), num_cmp, MRB_ARGS_REQ(1)); /* 15.2.9.3.1 */
mrb_define_method_id(mrb, fl, MRB_OPSYM(lt), num_lt, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, fl, MRB_OPSYM(le), num_le, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, fl, MRB_OPSYM(gt), num_gt, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, fl, MRB_OPSYM(ge), num_ge, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, fl, MRB_OPSYM(eq), flo_eq, MRB_ARGS_REQ(1)); /* 15.2.9.3.2 */
mrb_define_method_id(mrb, fl, MRB_SYM(ceil), flo_ceil, MRB_ARGS_OPT(1)); /* 15.2.9.3.8 */
mrb_define_method_id(mrb, fl, MRB_SYM_Q(finite), flo_finite_p, MRB_ARGS_NONE()); /* 15.2.9.3.9 */
mrb_define_method_id(mrb, fl, MRB_SYM(floor), flo_floor, MRB_ARGS_OPT(1)); /* 15.2.9.3.10 */
mrb_define_method_id(mrb, fl, MRB_SYM_Q(infinite),flo_infinite_p, MRB_ARGS_NONE()); /* 15.2.9.3.11 */
mrb_define_method_id(mrb, fl, MRB_SYM(round), flo_round, MRB_ARGS_OPT(1)); /* 15.2.9.3.12 */
mrb_define_method_id(mrb, fl, MRB_SYM(to_f), mrb_obj_itself, MRB_ARGS_NONE()); /* 15.2.9.3.13 */
mrb_define_method_id(mrb, fl, MRB_SYM(to_i), flo_to_i, MRB_ARGS_NONE()); /* 15.2.9.3.14 */
mrb_define_method_id(mrb, fl, MRB_SYM(truncate), flo_truncate, MRB_ARGS_OPT(1)); /* 15.2.9.3.15 */
mrb_define_method_id(mrb, fl, MRB_SYM(divmod), flo_divmod, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, fl, MRB_SYM(to_s), flo_to_s, MRB_ARGS_NONE()); /* 15.2.9.3.16(x) */
mrb_define_method_id(mrb, fl, MRB_SYM(inspect), flo_to_s, MRB_ARGS_NONE());
mrb_define_method_id(mrb, fl, MRB_SYM_Q(nan), flo_nan_p, MRB_ARGS_NONE());
mrb_define_method_id(mrb, fl, MRB_SYM(abs), flo_abs, MRB_ARGS_NONE()); /* 15.2.7.4.3 */
mrb_define_method_id(mrb, fl, MRB_SYM(hash), flo_hash, MRB_ARGS_NONE());
#ifdef INFINITY
mrb_define_const_id(mrb, fl, MRB_SYM(INFINITY), mrb_float_value(mrb, INFINITY));
#endif
#ifdef NAN
mrb_define_const_id(mrb, fl, MRB_SYM(NAN), mrb_float_value(mrb, NAN));
#endif
#endif
}