diff --git a/ChangeLog b/ChangeLog index 9d17e7c279..98b6ee8aaf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +Tue May 31 23:43:00 2011 Kenta Murata + + * ext/bigdecimal/bigdecimal.c (GetVpValueWithPrec): replace the algorithm for + coercing from a Rational to stop requiring "bigecimal/util.rb". + [ruby-core:34318] + + * ext/bigdecimal/bigdecimal.c (GetVpValue): refactoring. + + * ext/bigdecimal/bigdecimal.c (BigDecimal_new): support instantiation from a + Rational. + + * test/bigdecimal/test_bigdecimal.rb (test_global_new_with_rationao): add a + test for the above change. + + * test/bigdecimal/test_bigdecimal.rb (test_new_with_rationao): ditto. + Tue May 31 22:44:00 2011 Kenta Murata * ext/bigdecimal/bigdecimal.c (BigDecimal_new): support instantiation a diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 45dac45645..cd960f4b87 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -131,11 +131,13 @@ ToValue(Real *p) return p->obj; } -static Real * -GetVpValue(VALUE v, int must) +static VALUE BigDecimal_div2(int, VALUE*, VALUE); + +static Real* +GetVpValueWithPrec(VALUE v, long prec, int must) { Real *pv; - VALUE bg; + VALUE num, bg, args[2]; char szD[128]; VALUE orig = Qundef; int util_loaded = 0; @@ -143,54 +145,71 @@ GetVpValue(VALUE v, int must) again: switch(TYPE(v)) { - case T_RATIONAL: - if(orig == Qundef ? (orig = v, 1) : orig != v) { - if(!util_loaded) { - rb_require("bigdecimal/util"); - util_loaded = 1; - } - v = rb_funcall2(v, rb_intern("to_d"), 0, 0); - goto again; - } - v = orig; - goto SomeOneMayDoIt; + case T_RATIONAL: + if (prec < 0) goto unable_to_coerce_without_prec; - case T_DATA: - if(rb_typeddata_is_kind_of(v, &BigDecimal_data_type)) { - pv = DATA_PTR(v); - return pv; - } else { - goto SomeOneMayDoIt; - } - break; - case T_FIXNUM: - sprintf(szD, "%ld", FIX2LONG(v)); - return VpCreateRbObject(VpBaseFig() * 2 + 1, szD); + if (orig == Qundef ? (orig = v, 1) : orig != v) { + num = RRATIONAL(v)->num; + pv = GetVpValueWithPrec(num, -1, must); + if (pv == NULL) goto SomeOneMayDoIt; + + args[0] = RRATIONAL(v)->den; + args[1] = LONG2NUM(prec); + v = BigDecimal_div2(2, args, ToValue(pv)); + goto again; + } + + v = orig; + goto SomeOneMayDoIt; + + case T_DATA: + if (rb_typeddata_is_kind_of(v, &BigDecimal_data_type)) { + pv = DATA_PTR(v); + return pv; + } + else { + goto SomeOneMayDoIt; + } + break; + case T_FIXNUM: + sprintf(szD, "%ld", FIX2LONG(v)); + return VpCreateRbObject(VpBaseFig() * 2 + 1, szD); #ifdef ENABLE_NUMERIC_STRING - case T_STRING: - SafeStringValue(v); - return VpCreateRbObject(strlen(RSTRING_PTR(v)) + VpBaseFig() + 1, - RSTRING_PTR(v)); + case T_STRING: + SafeStringValue(v); + return VpCreateRbObject(strlen(RSTRING_PTR(v)) + VpBaseFig() + 1, + RSTRING_PTR(v)); #endif /* ENABLE_NUMERIC_STRING */ - case T_BIGNUM: - bg = rb_big2str(v, 10); - return VpCreateRbObject(strlen(RSTRING_PTR(bg)) + VpBaseFig() + 1, - RSTRING_PTR(bg)); - default: - goto SomeOneMayDoIt; + case T_BIGNUM: + bg = rb_big2str(v, 10); + return VpCreateRbObject(strlen(RSTRING_PTR(bg)) + VpBaseFig() + 1, + RSTRING_PTR(bg)); + default: + goto SomeOneMayDoIt; } SomeOneMayDoIt: - if(must) { - rb_raise(rb_eTypeError, "%s can't be coerced into BigDecimal", - rb_special_const_p(v)? - RSTRING_PTR(rb_inspect(v)): - rb_obj_classname(v) - ); + if (must) { + rb_raise(rb_eTypeError, "%s can't be coerced into BigDecimal", + rb_special_const_p(v) ? RSTRING_PTR(rb_inspect(v)) : rb_obj_classname(v)); } return NULL; /* NULL means to coerce */ + +unable_to_coerce_without_prec: + if (must) { + rb_raise(rb_eArgError, + "%s can't be coerced into BigDecimal without a precision", + rb_obj_classname(v)); + } + return NULL; +} + +static Real* +GetVpValue(VALUE v, int must) +{ + return GetVpValueWithPrec(v, -1, must); } /* call-seq: @@ -1775,6 +1794,12 @@ BigDecimal_new(int argc, VALUE *argv, VALUE self) case T_BIGNUM: return ToValue(GetVpValue(iniValue, 1)); + case T_RATIONAL: + if (NIL_P(nFig)) { + rb_raise(rb_eArgError, "can't omit precision for a Rational."); + } + return ToValue(GetVpValueWithPrec(iniValue, mf, 1)); + case T_STRING: /* fall through */ default: diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index d50b0e41c3..dd75dbfd64 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -36,6 +36,12 @@ class TestBigDecimal < Test::Unit::TestCase assert_equal(BigDecimal((-2**100).to_s), BigDecimal(-2**100)) end + def test_global_new_with_rational + assert_equal(BigDecimal("0.333333333333333333333"), BigDecimal(1.quo(3), 21)) + assert_equal(BigDecimal("-0.333333333333333333333"), BigDecimal(-1.quo(3), 21)) + assert_raise(ArgumentError) { BigDecimal(1.quo(3)) } + end + def test_new assert_equal(1, BigDecimal.new("1")) assert_equal(1, BigDecimal.new("1", 1)) @@ -58,6 +64,12 @@ class TestBigDecimal < Test::Unit::TestCase assert_equal(BigDecimal((-2**100).to_s), BigDecimal.new(-2**100)) end + def test_new_with_rational + assert_equal(BigDecimal("0.333333333333333333333"), BigDecimal.new(1.quo(3), 21)) + assert_equal(BigDecimal("-0.333333333333333333333"), BigDecimal.new(-1.quo(3), 21)) + assert_raise(ArgumentError) { BigDecimal.new(1.quo(3)) } + end + def _test_mode(type) BigDecimal.mode(type, true) assert_raise(FloatDomainError) { yield }