ruby-changes:64338
From: Kenta <ko1@a...>
Date: Sat, 19 Dec 2020 22:14:41 +0900 (JST)
Subject: [ruby-changes:64338] ff9e40811c (master): [bigdecimal] Add BigDecimal#precision
https://git.ruby-lang.org/ruby.git/commit/?id=ff9e40811c From ff9e40811c67b1090966b655f7adc0684fc58abe Mon Sep 17 00:00:00 2001 From: Kenta Murata <mrkn@m...> Date: Sat, 19 Dec 2020 04:24:15 +0900 Subject: [bigdecimal] Add BigDecimal#precision https://github.com/ruby/bigdecimal/commit/458eb66c49 diff --git a/ext/bigdecimal/bigdecimal.c b/ext/bigdecimal/bigdecimal.c index 039a4d7..ef975a0 100644 --- a/ext/bigdecimal/bigdecimal.c +++ b/ext/bigdecimal/bigdecimal.c @@ -378,6 +378,73 @@ BigDecimal_prec(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L378 } /* + * call-seq: + * big_decimal.precision -> intreger + * + * Returns the number of decimal digits in this number. + * + * Example: + * + * BigDecimal("0").precision # => 0 + * BigDecimal("1").precision # => 1 + * BigDecimal("-1e20").precision # => 21 + * BigDecimal("1e-20").precision # => 20 + * BigDecimal("Infinity").precision # => 0 + * BigDecimal("-Infinity").precision # => 0 + * BigDecimal("NaN").precision # => 0 + */ +static VALUE +BigDecimal_precision(VALUE self) +{ + ENTER(1); + + Real *p; + GUARD_OBJ(p, GetVpValue(self, 1)); + + /* + * The most significant digit is frac[0], and the least significant digit is frac[Prec-1]. + * When the exponent is zero, the decimal point is located just before frac[0]. + * When the exponent is negative, the decimal point moves to leftward. + * Conversely, when the exponent is positive, the decimal point moves to rightward. + * + * | frac[0] frac[1] frac[2] . frac[3] frac[4] ... frac[Prec-1] + * |------------------------> exponent == 3 + */ + + ssize_t ex = p->exponent; + ssize_t precision; + if (ex < 0) { + precision = (-ex + 1) * BASE_FIG; /* 1 is for p->frac[0] */ + ex = 0; + } + else if (p->Prec > 0) { + BDIGIT x = p->frac[0]; + for (precision = 0; x > 0; x /= 10) { + ++precision; + } + } + + if (ex > (ssize_t)p->Prec) { + precision += (ex - 1) * BASE_FIG; + } + else if (p->Prec > 0) { + ssize_t n = (ssize_t)p->Prec - 1; + while (n > 0 && p->frac[n] == 0) --n; + + precision += n * BASE_FIG; + + if (ex < (ssize_t)p->Prec) { + BDIGIT x = p->frac[n]; + for (; x > 0 && x % 10 == 0; x /= 10) { + --precision; + } + } + } + + return SSIZET2NUM(precision); +} + +/* * call-seq: hash * * Creates a hash for this BigDecimal. @@ -3509,6 +3576,7 @@ Init_bigdecimal(void) https://github.com/ruby/ruby/blob/trunk/ext/bigdecimal/bigdecimal.c#L3576 /* instance methods */ rb_define_method(rb_cBigDecimal, "precs", BigDecimal_prec, 0); + rb_define_method(rb_cBigDecimal, "precision", BigDecimal_precision, 0); rb_define_method(rb_cBigDecimal, "add", BigDecimal_add2, 2); rb_define_method(rb_cBigDecimal, "sub", BigDecimal_sub2, 2); diff --git a/test/bigdecimal/test_bigdecimal.rb b/test/bigdecimal/test_bigdecimal.rb index a2af553..59726ad 100644 --- a/test/bigdecimal/test_bigdecimal.rb +++ b/test/bigdecimal/test_bigdecimal.rb @@ -1903,6 +1903,55 @@ class TestBigDecimal < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/bigdecimal/test_bigdecimal.rb#L1903 EOS end + def test_precision_only_integer + assert_equal(0, BigDecimal(0).precision) + assert_equal(1, BigDecimal(1).precision) + assert_equal(1, BigDecimal(-1).precision) + assert_equal(2, BigDecimal(10).precision) + assert_equal(2, BigDecimal(-10).precision) + assert_equal(21, BigDecimal(100_000_000_000_000_000_000).precision) + assert_equal(21, BigDecimal(-100_000_000_000_000_000_000).precision) + assert_equal(103, BigDecimal("111e100").precision) + assert_equal(103, BigDecimal("-111e100").precision) + end + + def test_precision_only_fraction + assert_equal(1, BigDecimal("0.1").precision) + assert_equal(1, BigDecimal("-0.1").precision) + assert_equal(1, BigDecimal("0.01").precision) + assert_equal(1, BigDecimal("-0.01").precision) + assert_equal(2, BigDecimal("0.11").precision) + assert_equal(2, BigDecimal("-0.11").precision) + assert_equal(21, BigDecimal("0.000_000_000_000_000_000_001").precision) + assert_equal(21, BigDecimal("-0.000_000_000_000_000_000_001").precision) + assert_equal(100, BigDecimal("111e-100").precision) + assert_equal(100, BigDecimal("-111e-100").precision) + end + + def test_precision_full + assert_equal(1, BigDecimal("0.1").precision) + assert_equal(1, BigDecimal("-0.1").precision) + assert_equal(1, BigDecimal("0.01").precision) + assert_equal(1, BigDecimal("-0.01").precision) + assert_equal(2, BigDecimal("0.11").precision) + assert_equal(2, BigDecimal("-0.11").precision) + assert_equal(5, BigDecimal("11111e-2").precision) + assert_equal(5, BigDecimal("-11111e-2").precision) + assert_equal(21, BigDecimal("100.000_000_000_000_000_001").precision) + assert_equal(21, BigDecimal("-100.000_000_000_000_000_001").precision) + end + + def test_precision_special + BigDecimal.save_exception_mode do + BigDecimal.mode(BigDecimal::EXCEPTION_OVERFLOW, false) + BigDecimal.mode(BigDecimal::EXCEPTION_NaN, false) + + assert_equal(0, BigDecimal("Infinity").precision) + assert_equal(0, BigDecimal("-Infinity").precision) + assert_equal(0, BigDecimal("NaN").precision) + end + end + def test_initialize_copy_dup_clone_frozen_error bd = BigDecimal(1) bd2 = BigDecimal(2) -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/