1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

[ruby/bigdecimal] Use pre-allocated objects for special values

95c201f2d3
This commit is contained in:
Kenta Murata 2021-01-12 22:51:29 +09:00
parent 6670de82c2
commit 2175c2c957
2 changed files with 103 additions and 24 deletions

View file

@ -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)
{
@ -2782,10 +2788,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) {
@ -2801,19 +2824,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
@ -3423,6 +3435,46 @@ get_vp_value:
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.
*
@ -3694,13 +3746,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 infinity value. */
rb_define_const(rb_cBigDecimal, "INFINITY", f_BigDecimal(1, &arg, rb_cBigDecimal));
arg = rb_str_new2("NaN");
/* 'Not a Number' value. */
rb_define_const(rb_cBigDecimal, "NAN", f_BigDecimal(1, &arg, rb_cBigDecimal));
/* 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. */
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. */
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);

View file

@ -150,17 +150,23 @@ class TestBigDecimal < Test::Unit::TestCase
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