diff options
-rw-r--r-- | ext/bigdecimal/bigdecimal.c | 115 | ||||
-rw-r--r-- | test/bigdecimal/test_bigdecimal.rb | 8 |
2 files changed, 101 insertions, 22 deletions
diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 8414769c69..4997487572 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); * **** 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 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 b15b046f4c..e8fd85a526 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -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 |