Skip to content

Commit 83e3b3d

Browse files
committed
Implement BigDecimal#_decimal_shift for internal use
1 parent 73c1caf commit 83e3b3d

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
@@ -220,6 +220,7 @@ rbd_allocate_struct_one_nolimit(int sign, size_t const digits)
220220
static unsigned short VpGetException(void);
221221
static void VpSetException(unsigned short f);
222222
static void VpCheckException(Real *p, bool always);
223+
static int AddExponent(Real *a, SIGNED_VALUE n);
223224
static VALUE CheckGetValue(BDVALUE v);
224225
static void VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v);
225226
static int VpLimitRound(Real *c, size_t ixDigit);
@@ -2515,6 +2516,63 @@ BigDecimal_inspect(VALUE self)
25152516
return str;
25162517
}
25172518

2519+
/* Returns self * 10**v without changing the precision.
2520+
* This method is currently for internal use.
2521+
*
2522+
* BigDecimal("0.123e10")._decimal_shift(20) #=> "0.123e30"
2523+
* BigDecimal("0.123e10")._decimal_shift(-20) #=> "0.123e-10"
2524+
*/
2525+
static VALUE
2526+
BigDecimal_decimal_shift(VALUE self, VALUE v)
2527+
{
2528+
BDVALUE a, c;
2529+
ssize_t shift, exponentShift;
2530+
bool shiftDown;
2531+
size_t prec;
2532+
DECDIG ex, iex;
2533+
2534+
a = GetBDValueMust(self);
2535+
shift = NUM2SSIZET(rb_to_int(v));
2536+
2537+
if (VpIsZero(a.real) || VpIsNaN(a.real) || VpIsInf(a.real) || shift == 0) return CheckGetValue(a);
2538+
2539+
exponentShift = shift > 0 ? shift / BASE_FIG : (shift + 1) / BASE_FIG - 1;
2540+
shift -= exponentShift * BASE_FIG;
2541+
ex = 1;
2542+
for (int i = 0; i < shift; i++) ex *= 10;
2543+
shiftDown = a.real->frac[0] * (DECDIG_DBL)ex >= BASE;
2544+
iex = BASE / ex;
2545+
2546+
prec = a.real->Prec + shiftDown;
2547+
c = NewZeroWrap(1, prec * BASE_FIG);
2548+
if (shift == 0) {
2549+
VpAsgn(c.real, a.real, 1);
2550+
} else if (shiftDown) {
2551+
DECDIG carry = 0;
2552+
exponentShift++;
2553+
for (size_t i = 0; i < a.real->Prec; i++) {
2554+
DECDIG v = a.real->frac[i];
2555+
c.real->frac[i] = carry * ex + v / iex;
2556+
carry = v % iex;
2557+
}
2558+
c.real->frac[a.real->Prec] = carry * ex;
2559+
} else {
2560+
DECDIG carry = 0;
2561+
for (ssize_t i = a.real->Prec - 1; i >= 0; i--) {
2562+
DECDIG v = a.real->frac[i];
2563+
c.real->frac[i] = v % iex * ex + carry;
2564+
carry = v / iex;
2565+
}
2566+
}
2567+
while (c.real->frac[prec - 1] == 0) prec--;
2568+
c.real->Prec = prec;
2569+
c.real->sign = a.real->sign;
2570+
c.real->exponent = a.real->exponent;
2571+
AddExponent(c.real, exponentShift);
2572+
RB_GC_GUARD(a.bigdecimal);
2573+
return CheckGetValue(c);
2574+
}
2575+
25182576
inline static int
25192577
is_zero(VALUE x)
25202578
{
@@ -3627,6 +3685,7 @@ Init_bigdecimal(void)
36273685
rb_define_method(rb_cBigDecimal, "infinite?", BigDecimal_IsInfinite, 0);
36283686
rb_define_method(rb_cBigDecimal, "finite?", BigDecimal_IsFinite, 0);
36293687
rb_define_method(rb_cBigDecimal, "truncate", BigDecimal_truncate, -1);
3688+
rb_define_method(rb_cBigDecimal, "_decimal_shift", BigDecimal_decimal_shift, 1);
36303689
rb_define_method(rb_cBigDecimal, "_dump", BigDecimal_dump, -1);
36313690

36323691
#ifdef BIGDECIMAL_USE_VP_TEST_METHODS
@@ -3687,7 +3746,6 @@ enum op_sw {
36873746
};
36883747

36893748
static int VpIsDefOP(Real *c, Real *a, Real *b, enum op_sw sw);
3690-
static int AddExponent(Real *a, SIGNED_VALUE n);
36913749
static DECDIG VpAddAbs(Real *a,Real *b,Real *c);
36923750
static DECDIG VpSubAbs(Real *a,Real *b,Real *c);
36933751
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
@@ -1466,6 +1466,23 @@ def test_sqrt_5266
14661466
x.sqrt(109).to_s(109).split(' ')[0])
14671467
end
14681468

1469+
def test_internal_use_decimal_shift
1470+
assert_positive_infinite_calculation { BigDecimal::INFINITY._decimal_shift(10) }
1471+
assert_negative_infinite_calculation { NEGATIVE_INFINITY._decimal_shift(10) }
1472+
assert_nan_calculation { BigDecimal::NAN._decimal_shift(10) }
1473+
assert_equal(BigDecimal(0), BigDecimal(0)._decimal_shift(10))
1474+
[
1475+
BigDecimal('-1234.56789'),
1476+
BigDecimal('123456789.012345678987654321'),
1477+
BigDecimal('123456789012345.678987654321')
1478+
].each do |num|
1479+
(0..20).each do |shift|
1480+
assert_equal(num * 10**shift, num._decimal_shift(shift))
1481+
assert_equal(num / 10**shift, num._decimal_shift(-shift))
1482+
end
1483+
end
1484+
end
1485+
14691486
def test_fix
14701487
x = BigDecimal("1.1")
14711488
assert_equal(1, x.fix)

0 commit comments

Comments
 (0)