Skip to content

Commit a837536

Browse files
committed
Implement BigDecimal#_decimal_shift for internal use
1 parent beb3e1e commit a837536

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

ext/bigdecimal/bigdecimal.c

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ rbd_allocate_struct_one_nolimit(int sign, size_t const digits)
269269
static unsigned short VpGetException(void);
270270
static void VpSetException(unsigned short f);
271271
static void VpCheckException(Real *p, bool always);
272+
static int AddExponent(Real *a, SIGNED_VALUE n);
272273
static VALUE CheckGetValue(BDVALUE v);
273274
static void VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v);
274275
static int VpLimitRound(Real *c, size_t ixDigit);
@@ -2672,6 +2673,63 @@ BigDecimal_inspect(VALUE self)
26722673
return str;
26732674
}
26742675

2676+
/* Returns self * 10**v without changing the precision.
2677+
* This method is currently for internal use.
2678+
*
2679+
* BigDecimal("0.123e10")._decimal_shift(20) #=> "0.123e30"
2680+
* BigDecimal("0.123e10")._decimal_shift(-20) #=> "0.123e-10"
2681+
*/
2682+
static VALUE
2683+
BigDecimal_decimal_shift(VALUE self, VALUE v)
2684+
{
2685+
BDVALUE a, c;
2686+
ssize_t shift, exponentShift;
2687+
bool shiftDown;
2688+
size_t prec;
2689+
DECDIG ex, iex;
2690+
2691+
a = GetBDValueMust(self);
2692+
shift = NUM2SSIZET(rb_to_int(v));
2693+
2694+
if (VpIsZero(a.real) || VpIsNaN(a.real) || VpIsInf(a.real) || shift == 0) return CheckGetValue(a);
2695+
2696+
exponentShift = shift > 0 ? shift / BASE_FIG : (shift + 1) / BASE_FIG - 1;
2697+
shift -= exponentShift * BASE_FIG;
2698+
ex = 1;
2699+
for (int i = 0; i < shift; i++) ex *= 10;
2700+
shiftDown = a.real->frac[0] * (DECDIG_DBL)ex >= BASE;
2701+
iex = BASE / ex;
2702+
2703+
prec = a.real->Prec + shiftDown;
2704+
c = NewZeroWrapLimited(1, prec * BASE_FIG);
2705+
if (shift == 0) {
2706+
VpAsgn(c.real, a.real, 1);
2707+
} else if (shiftDown) {
2708+
DECDIG carry = 0;
2709+
exponentShift++;
2710+
for (size_t i = 0; i < a.real->Prec; i++) {
2711+
DECDIG v = a.real->frac[i];
2712+
c.real->frac[i] = carry * ex + v / iex;
2713+
carry = v % iex;
2714+
}
2715+
c.real->frac[a.real->Prec] = carry * ex;
2716+
} else {
2717+
DECDIG carry = 0;
2718+
for (ssize_t i = a.real->Prec - 1; i >= 0; i--) {
2719+
DECDIG v = a.real->frac[i];
2720+
c.real->frac[i] = v % iex * ex + carry;
2721+
carry = v / iex;
2722+
}
2723+
}
2724+
while (c.real->frac[prec - 1] == 0) prec--;
2725+
c.real->Prec = prec;
2726+
c.real->sign = a.real->sign;
2727+
c.real->exponent = a.real->exponent;
2728+
AddExponent(c.real, exponentShift);
2729+
RB_GC_GUARD(a.bigdecimal);
2730+
return CheckGetValue(c);
2731+
}
2732+
26752733
inline static int
26762734
is_zero(VALUE x)
26772735
{
@@ -3796,6 +3854,7 @@ Init_bigdecimal(void)
37963854
rb_define_method(rb_cBigDecimal, "infinite?", BigDecimal_IsInfinite, 0);
37973855
rb_define_method(rb_cBigDecimal, "finite?", BigDecimal_IsFinite, 0);
37983856
rb_define_method(rb_cBigDecimal, "truncate", BigDecimal_truncate, -1);
3857+
rb_define_method(rb_cBigDecimal, "_decimal_shift", BigDecimal_decimal_shift, 1);
37993858
rb_define_method(rb_cBigDecimal, "_dump", BigDecimal_dump, -1);
38003859

38013860
#ifdef BIGDECIMAL_USE_VP_TEST_METHODS
@@ -3856,7 +3915,6 @@ enum op_sw {
38563915
};
38573916

38583917
static int VpIsDefOP(Real *c, Real *a, Real *b, enum op_sw sw);
3859-
static int AddExponent(Real *a, SIGNED_VALUE n);
38603918
static DECDIG VpAddAbs(Real *a,Real *b,Real *c);
38613919
static DECDIG VpSubAbs(Real *a,Real *b,Real *c);
38623920
static size_t VpSetPTR(Real *a, Real *b, Real *c, size_t *a_pos, size_t *b_pos, size_t *c_pos, DECDIG *av, DECDIG *bv);

test/bigdecimal/test_bigdecimal.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,6 +1420,23 @@ def test_sqrt_5266
14201420
x.sqrt(109).to_s(109).split(' ')[0])
14211421
end
14221422

1423+
def test_internal_use_decimal_shift
1424+
assert_positive_infinite_calculation { BigDecimal::INFINITY._decimal_shift(10) }
1425+
assert_negative_infinite_calculation { NEGATIVE_INFINITY._decimal_shift(10) }
1426+
assert_nan_calculation { BigDecimal::NAN._decimal_shift(10) }
1427+
assert_equal(BigDecimal(0), BigDecimal(0)._decimal_shift(10))
1428+
[
1429+
BigDecimal('-1234.56789'),
1430+
BigDecimal('123456789.012345678987654321'),
1431+
BigDecimal('123456789012345.678987654321')
1432+
].each do |num|
1433+
(0..20).each do |shift|
1434+
assert_equal(num * 10**shift, num._decimal_shift(shift))
1435+
assert_equal(num / 10**shift, num._decimal_shift(-shift))
1436+
end
1437+
end
1438+
end
1439+
14231440
def test_fix
14241441
x = BigDecimal("1.1")
14251442
assert_equal(1, x.fix)

0 commit comments

Comments
 (0)