Skip to content

Commit

Permalink
Use pre-allocated objects for special values
Browse files Browse the repository at this point in the history
  • Loading branch information
mrkn committed Jan 12, 2021
1 parent d163f17 commit 95c201f
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 22 deletions.
115 changes: 94 additions & 21 deletions ext/bigdecimal/bigdecimal.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,12 @@ static int VPrint(FILE *fp,const char *cntl_chr,Real *a);
* **** BigDecimal part ****
*/

static VALUE BigDecimal_nan(void);
static VALUE BigDecimal_positive_infinity(void);
static VALUE BigDecimal_negative_infinity(void);
static VALUE BigDecimal_positive_zero(void);
static VALUE BigDecimal_negative_zero(void);

static void
BigDecimal_delete(void *pv)
{
Expand Down Expand Up @@ -2785,10 +2791,27 @@ rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)

double d = RFLOAT_VALUE(val);

if (!isfinite(d)) {
Real *vp = VpCreateRbObject(1, NULL, true); /* vp->obj is allocated */
VpDtoV(vp, d);
return check_exception(vp->obj);
if (isnan(d)) {
VALUE obj = BigDecimal_nan();
return check_exception(obj);
}
else if (isinf(d)) {
VALUE obj;
if (d > 0) {
obj = BigDecimal_positive_infinity();
}
else {
obj = BigDecimal_negative_infinity();
}
return check_exception(obj);
}
else if (d == 0.0) {
if (1/d < 0.0) {
return BigDecimal_negative_zero();
}
else {
return BigDecimal_positive_zero();
}
}

if (digs == SIZE_MAX) {
Expand All @@ -2804,19 +2827,8 @@ rb_float_convert_to_BigDecimal(VALUE val, size_t digs, int raise_exception)
rb_raise(rb_eArgError, "precision too large.");
}

if (d != 0.0) {
val = rb_funcall(val, id_to_r, 0);
return rb_rational_convert_to_BigDecimal(val, digs, raise_exception);
}

Real *vp;
if (1/d < 0.0) {
vp = VpCreateRbObject(digs, "-0", true);
}
else {
vp = VpCreateRbObject(digs, "0", true);
}
return check_exception(vp->obj);
val = rb_funcall(val, id_to_r, 0);
return rb_rational_convert_to_BigDecimal(val, digs, raise_exception);
}

static VALUE
Expand Down Expand Up @@ -3426,6 +3438,46 @@ BigMath_s_log(VALUE klass, VALUE x, VALUE vprec)
return y;
}

static VALUE BIGDECIMAL_NAN = Qnil;

static VALUE
BigDecimal_nan(void)
{
return BIGDECIMAL_NAN;
}

static VALUE BIGDECIMAL_POSITIVE_INFINITY = Qnil;

static VALUE
BigDecimal_positive_infinity(void)
{
return BIGDECIMAL_POSITIVE_INFINITY;
}

static VALUE BIGDECIMAL_NEGATIVE_INFINITY = Qnil;

static VALUE
BigDecimal_negative_infinity(void)
{
return BIGDECIMAL_NEGATIVE_INFINITY;
}

static VALUE BIGDECIMAL_POSITIVE_ZERO = Qnil;

static VALUE
BigDecimal_positive_zero(void)
{
return BIGDECIMAL_POSITIVE_ZERO;
}

static VALUE BIGDECIMAL_NEGATIVE_ZERO = Qnil;

static VALUE
BigDecimal_negative_zero(void)
{
return BIGDECIMAL_NEGATIVE_ZERO;
}

/* Document-class: BigDecimal
* BigDecimal provides arbitrary-precision floating point decimal arithmetic.
*
Expand Down Expand Up @@ -3697,13 +3749,34 @@ Init_bigdecimal(void)
/* -3: Indicates that a value is negative and infinite. See BigDecimal.sign. */
rb_define_const(rb_cBigDecimal, "SIGN_NEGATIVE_INFINITE", INT2FIX(VP_SIGN_NEGATIVE_INFINITE));

arg = rb_str_new2("+Infinity");
/* Positive zero value. */
arg = rb_str_new2("+0");
BIGDECIMAL_POSITIVE_ZERO = f_BigDecimal(1, &arg, rb_cBigDecimal);
rb_gc_register_mark_object(BIGDECIMAL_POSITIVE_ZERO);

/* Negative zero value. */
arg = rb_str_new2("-0");
BIGDECIMAL_NEGATIVE_ZERO = f_BigDecimal(1, &arg, rb_cBigDecimal);
rb_gc_register_mark_object(BIGDECIMAL_NEGATIVE_ZERO);

/* Positive infinity value. */
rb_define_const(rb_cBigDecimal, "INFINITY", f_BigDecimal(1, &arg, rb_cBigDecimal));
arg = rb_str_new2("NaN");
arg = rb_str_new2("+Infinity");
BIGDECIMAL_POSITIVE_INFINITY = f_BigDecimal(1, &arg, rb_cBigDecimal);
rb_gc_register_mark_object(BIGDECIMAL_POSITIVE_INFINITY);

/* Negative infinity value. */
arg = rb_str_new2("-Infinity");
BIGDECIMAL_NEGATIVE_INFINITY = f_BigDecimal(1, &arg, rb_cBigDecimal);
rb_gc_register_mark_object(BIGDECIMAL_NEGATIVE_INFINITY);

/* 'Not a Number' value. */
rb_define_const(rb_cBigDecimal, "NAN", f_BigDecimal(1, &arg, rb_cBigDecimal));
arg = rb_str_new2("NaN");
BIGDECIMAL_NAN = f_BigDecimal(1, &arg, rb_cBigDecimal);
rb_gc_register_mark_object(BIGDECIMAL_NAN);

/* Special value constants */
rb_define_const(rb_cBigDecimal, "INFINITY", BIGDECIMAL_POSITIVE_INFINITY);
rb_define_const(rb_cBigDecimal, "NAN", BIGDECIMAL_NAN);

/* instance methods */
rb_define_method(rb_cBigDecimal, "precs", BigDecimal_prec, 0);
Expand Down
8 changes: 7 additions & 1 deletion test/bigdecimal/test_bigdecimal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -151,17 +151,23 @@ def test_BigDecimal_with_float
assert_raise(ArgumentError) { BigDecimal(0.1, Float::DIG + 2) }
assert_nothing_raised { BigDecimal(0.1, Float::DIG + 1) }

assert_same(BigDecimal(0.0), BigDecimal(0.0))
assert_same(BigDecimal(-0.0), BigDecimal(-0.0))

bug9214 = '[ruby-core:58858]'
assert_equal(BigDecimal(-0.0, Float::DIG).sign, -1, bug9214)
assert_equal(BigDecimal(-0.0).sign, -1, bug9214)

BigDecimal.save_exception_mode do
BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false)
assert_nan(BigDecimal(Float::NAN))
assert_same(BigDecimal(Float::NAN), BigDecimal(Float::NAN))
end
BigDecimal.save_exception_mode do
BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
assert_positive_infinite(BigDecimal(Float::INFINITY))
assert_same(BigDecimal(Float::INFINITY), BigDecimal(Float::INFINITY))
assert_negative_infinite(BigDecimal(-Float::INFINITY))
assert_same(BigDecimal(-Float::INFINITY), BigDecimal(-Float::INFINITY))
end
end

Expand Down

0 comments on commit 95c201f

Please sign in to comment.