Skip to content

Commit 12ff693

Browse files
committed
Implement BigDecimal#_decimal_shift for internal use
1 parent f6bfd2f commit 12ff693

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);
@@ -2514,6 +2515,63 @@ BigDecimal_inspect(VALUE self)
25142515
return str;
25152516
}
25162517

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

36313690
#ifdef BIGDECIMAL_USE_VP_TEST_METHODS
@@ -3686,7 +3745,6 @@ enum op_sw {
36863745
};
36873746

36883747
static int VpIsDefOP(Real *c, Real *a, Real *b, enum op_sw sw);
3689-
static int AddExponent(Real *a, SIGNED_VALUE n);
36903748
static DECDIG VpAddAbs(Real *a,Real *b,Real *c);
36913749
static DECDIG VpSubAbs(Real *a,Real *b,Real *c);
36923750
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)