ruby-changes:64846
From: Kenta <ko1@a...>
Date: Wed, 13 Jan 2021 02:04:17 +0900 (JST)
Subject: [ruby-changes:64846] 2175c2c957 (master): [ruby/bigdecimal] Use pre-allocated objects for special values
https://git.ruby-lang.org/ruby.git/commit/?id=2175c2c957 From 2175c2c957a711f1c48f42d38db202bf52c8bc6f Mon Sep 17 00:00:00 2001 From: Kenta Murata <mrkn@m...> Date: Tue, 12 Jan 2021 22:51:29 +0900 Subject: [ruby/bigdecimal] Use pre-allocated objects for special values https://github.com/ruby/bigdecimal/commit/95c201f2d3 diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 8414769..4997487 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -127,6 +127,12 @@ static int VPrint(FILE *fp,const char *cntl_chr,Real *a); https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L127 * **** 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) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L2788 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) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L2824 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: https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L3435 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) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L3746 /* -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); diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index b15b046..e8fd85a 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -150,17 +150,23 @@ class TestBigDecimal < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/bigdecimal/test_bigdecimal.rb#L150 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 -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/