Skip to content

Commit a5f23ae

Browse files
committed
Implement BigDecimal#_decimal_shift for internal use
1 parent b6d2987 commit a5f23ae

File tree

2 files changed

+77
-0
lines changed

2 files changed

+77
-0
lines changed

ext/bigdecimal/bigdecimal.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2829,6 +2829,64 @@ BigDecimal_inspect(VALUE self)
28292829
return str;
28302830
}
28312831

2832+
/* Returns self * 10**v without changing the precision.
2833+
* This method is currently for internal use.
2834+
*
2835+
* BigDecimal("0.123e10")._decimal_shift(20) #=> "0.123e30"
2836+
* BigDecimal("0.123e10")._decimal_shift(-20) #=> "0.123e-10"
2837+
*/
2838+
static VALUE
2839+
BigDecimal_decimal_shift(VALUE self, VALUE v)
2840+
{
2841+
ENTER(5);
2842+
Real *c, *a;
2843+
2844+
v = rb_to_int(v);
2845+
if (!FIXNUM_P(v)) {
2846+
rb_raise(rb_eTypeError, "invalid shift width");
2847+
}
2848+
long shift = FIX2LONG(v);
2849+
if (shift == 0) return self;
2850+
2851+
GUARD_OBJ(a, GetVpValue(self, 1));
2852+
if (VpIsZero(a) || VpIsNaN(a) || VpIsInf(a)) return self;
2853+
2854+
long exponentShift = shift > 0 ? shift / BASE_FIG : (shift + 1) / BASE_FIG - 1;
2855+
shift -= exponentShift * BASE_FIG;
2856+
DECDIG ex = 1;
2857+
for (int i = 0; i < shift; i++) ex *= 10;
2858+
bool shiftDown = a->frac[0] * (DECDIG_DBL)ex >= BASE;
2859+
DECDIG iex = BASE / ex;
2860+
2861+
size_t prec = a->Prec + shiftDown;
2862+
size_t mx = prec * (VpBaseFig() + 1);
2863+
GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
2864+
if (shift == 0) {
2865+
VpAsgn(c, a, 1);
2866+
} else if (shiftDown) {
2867+
exponentShift++;
2868+
DECDIG carry = 0;
2869+
for (size_t i = 0; i < a->Prec; i++) {
2870+
DECDIG v = a->frac[i];
2871+
c->frac[i] = carry * ex + v / iex;
2872+
carry = v % iex;
2873+
}
2874+
c->frac[a->Prec] = carry * ex;
2875+
} else {
2876+
DECDIG carry = 0;
2877+
for (ssize_t i = a->Prec - 1; i >= 0; i--) {
2878+
DECDIG v = a->frac[i];
2879+
c->frac[i] = v % iex * ex + carry;
2880+
carry = v / iex;
2881+
}
2882+
}
2883+
while (c->frac[prec - 1] == 0) prec--;
2884+
c->Prec = prec;
2885+
c->exponent = a->exponent + exponentShift;
2886+
c->sign = a->sign;
2887+
return VpCheckGetValue(c);
2888+
}
2889+
28322890
static VALUE BigMath_s_exp(VALUE, VALUE, VALUE);
28332891
static VALUE BigMath_s_log(VALUE, VALUE, VALUE);
28342892

@@ -4582,6 +4640,7 @@ Init_bigdecimal(void)
45824640
rb_define_method(rb_cBigDecimal, "infinite?", BigDecimal_IsInfinite, 0);
45834641
rb_define_method(rb_cBigDecimal, "finite?", BigDecimal_IsFinite, 0);
45844642
rb_define_method(rb_cBigDecimal, "truncate", BigDecimal_truncate, -1);
4643+
rb_define_method(rb_cBigDecimal, "_decimal_shift", BigDecimal_decimal_shift, 1);
45854644
rb_define_method(rb_cBigDecimal, "_dump", BigDecimal_dump, -1);
45864645

45874646
rb_mBigMath = rb_define_module("BigMath");

test/bigdecimal/test_bigdecimal.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1253,6 +1253,24 @@ def test_sqrt_5266
12531253
x.sqrt(109).to_s(109).split(' ')[0])
12541254
end
12551255

1256+
def test_internal_use_decimal_shift
1257+
BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
1258+
assert_positive_infinite(BigDecimal("Infinity")._decimal_shift(10))
1259+
assert_negative_infinite(BigDecimal("-Infinity")._decimal_shift(10))
1260+
assert_nan(BigDecimal::NAN._decimal_shift(10))
1261+
assert_equal(BigDecimal(0), BigDecimal(0)._decimal_shift(10))
1262+
[
1263+
BigDecimal('-1234.56789'),
1264+
BigDecimal('123456789.012345678987654321'),
1265+
BigDecimal('1234567890123.45678987654321')
1266+
].each do |num|
1267+
(0..20).each do |shift|
1268+
assert_equal(num * 10**shift, num._decimal_shift(shift))
1269+
assert_equal(num / 10**shift, num._decimal_shift(-shift))
1270+
end
1271+
end
1272+
end
1273+
12561274
def test_fix
12571275
x = BigDecimal("1.1")
12581276
assert_equal(1, x.fix)

0 commit comments

Comments
 (0)