Skip to content

Commit 782c96c

Browse files
committed
Implement BigDecimal#_decimal_shift for internal use
1 parent 07facd4 commit 782c96c

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
@@ -2879,6 +2879,64 @@ BigDecimal_inspect(VALUE self)
28792879
return str;
28802880
}
28812881

2882+
/* Returns self * 10**v without changing the precision.
2883+
* This method is currently for internal use.
2884+
*
2885+
* BigDecimal("0.123e10")._decimal_shift(20) #=> "0.123e30"
2886+
* BigDecimal("0.123e10")._decimal_shift(-20) #=> "0.123e-10"
2887+
*/
2888+
static VALUE
2889+
BigDecimal_decimal_shift(VALUE self, VALUE v)
2890+
{
2891+
ENTER(5);
2892+
Real *c, *a;
2893+
2894+
v = rb_to_int(v);
2895+
if (!FIXNUM_P(v)) {
2896+
rb_raise(rb_eTypeError, "invalid shift width");
2897+
}
2898+
long shift = FIX2LONG(v);
2899+
if (shift == 0) return self;
2900+
2901+
GUARD_OBJ(a, GetVpValue(self, 1));
2902+
if (VpIsZero(a) || VpIsNaN(a) || VpIsInf(a)) return self;
2903+
2904+
long exponentShift = shift > 0 ? shift / BASE_FIG : (shift + 1) / BASE_FIG - 1;
2905+
shift -= exponentShift * BASE_FIG;
2906+
DECDIG ex = 1;
2907+
for (int i = 0; i < shift; i++) ex *= 10;
2908+
bool shiftDown = a->frac[0] * (DECDIG_DBL)ex >= BASE;
2909+
DECDIG iex = BASE / ex;
2910+
2911+
size_t prec = a->Prec + shiftDown;
2912+
size_t mx = prec * (VpBaseFig() + 1);
2913+
GUARD_OBJ(c, NewZeroWrapLimited(1, mx));
2914+
if (shift == 0) {
2915+
VpAsgn(c, a, 1);
2916+
} else if (shiftDown) {
2917+
exponentShift++;
2918+
DECDIG carry = 0;
2919+
for (size_t i = 0; i < a->Prec; i++) {
2920+
DECDIG v = a->frac[i];
2921+
c->frac[i] = carry * ex + v / iex;
2922+
carry = v % iex;
2923+
}
2924+
c->frac[a->Prec] = carry * ex;
2925+
} else {
2926+
DECDIG carry = 0;
2927+
for (ssize_t i = a->Prec - 1; i >= 0; i--) {
2928+
DECDIG v = a->frac[i];
2929+
c->frac[i] = v % iex * ex + carry;
2930+
carry = v / iex;
2931+
}
2932+
}
2933+
while (c->frac[prec - 1] == 0) prec--;
2934+
c->Prec = prec;
2935+
c->exponent = a->exponent + exponentShift;
2936+
c->sign = a->sign;
2937+
return VpCheckGetValue(c);
2938+
}
2939+
28822940
static VALUE BigMath_s_exp(VALUE, VALUE, VALUE);
28832941
static VALUE BigMath_s_log(VALUE, VALUE, VALUE);
28842942

@@ -4633,6 +4691,7 @@ Init_bigdecimal(void)
46334691
rb_define_method(rb_cBigDecimal, "infinite?", BigDecimal_IsInfinite, 0);
46344692
rb_define_method(rb_cBigDecimal, "finite?", BigDecimal_IsFinite, 0);
46354693
rb_define_method(rb_cBigDecimal, "truncate", BigDecimal_truncate, -1);
4694+
rb_define_method(rb_cBigDecimal, "_decimal_shift", BigDecimal_decimal_shift, 1);
46364695
rb_define_method(rb_cBigDecimal, "_dump", BigDecimal_dump, -1);
46374696

46384697
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
@@ -1200,6 +1200,24 @@ def test_sqrt_5266
12001200
x.sqrt(109).to_s(109).split(' ')[0])
12011201
end
12021202

1203+
def test_internal_use_decimal_shift
1204+
BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
1205+
assert_positive_infinite(BigDecimal("Infinity")._decimal_shift(10))
1206+
assert_negative_infinite(BigDecimal("-Infinity")._decimal_shift(10))
1207+
assert_nan(BigDecimal::NAN._decimal_shift(10))
1208+
assert_equal(BigDecimal(0), BigDecimal(0)._decimal_shift(10))
1209+
[
1210+
BigDecimal('-1234.56789'),
1211+
BigDecimal('123456789.012345678987654321'),
1212+
BigDecimal('1234567890123.45678987654321')
1213+
].each do |num|
1214+
(0..20).each do |shift|
1215+
assert_equal(num * 10**shift, num._decimal_shift(shift))
1216+
assert_equal(num / 10**shift, num._decimal_shift(-shift))
1217+
end
1218+
end
1219+
end
1220+
12031221
def test_fix
12041222
x = BigDecimal("1.1")
12051223
assert_equal(1, x.fix)

0 commit comments

Comments
 (0)