Skip to content

Commit 34f5524

Browse files
committed
Implement BigDecimal#_decimal_shift for internal use
1 parent 0d854c4 commit 34f5524

File tree

2 files changed

+78
-1
lines changed

2 files changed

+78
-1
lines changed

ext/bigdecimal/bigdecimal.c

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ rbd_allocate_struct_one_nolimit(int sign, size_t const digits)
302302
static unsigned short VpGetException(void);
303303
static void VpSetException(unsigned short f);
304304
static void VpCheckException(Real *p, bool always);
305+
static int AddExponent(Real *a, SIGNED_VALUE n);
305306
static VALUE CheckGetValue(BDVALUE v);
306307
static void VpInternalRound(Real *c, size_t ixDigit, DECDIG vPrev, DECDIG v);
307308
static int VpLimitRound(Real *c, size_t ixDigit);
@@ -2816,6 +2817,64 @@ BigDecimal_inspect(VALUE self)
28162817
return str;
28172818
}
28182819

2820+
/* Returns self * 10**v without changing the precision.
2821+
* This method is currently for internal use.
2822+
*
2823+
* BigDecimal("0.123e10")._decimal_shift(20) #=> "0.123e30"
2824+
* BigDecimal("0.123e10")._decimal_shift(-20) #=> "0.123e-10"
2825+
*/
2826+
static VALUE
2827+
BigDecimal_decimal_shift(VALUE self, VALUE v)
2828+
{
2829+
ENTER(5);
2830+
BDVALUE a, c;
2831+
ssize_t shift, exponentShift;
2832+
bool shiftDown;
2833+
size_t prec;
2834+
DECDIG ex, iex;
2835+
2836+
shift = NUM2SSIZET(rb_to_int(v));
2837+
if (shift == 0) return self;
2838+
2839+
GUARD_OBJ(a, GetBDValueMust(self));
2840+
if (VpIsZero(a.real) || VpIsNaN(a.real) || VpIsInf(a.real)) return self;
2841+
2842+
exponentShift = shift > 0 ? shift / BASE_FIG : (shift + 1) / BASE_FIG - 1;
2843+
shift -= exponentShift * BASE_FIG;
2844+
ex = 1;
2845+
for (int i = 0; i < shift; i++) ex *= 10;
2846+
shiftDown = a.real->frac[0] * (DECDIG_DBL)ex >= BASE;
2847+
iex = BASE / ex;
2848+
2849+
prec = a.real->Prec + shiftDown;
2850+
GUARD_OBJ(c, NewZeroWrapLimited(1, prec * BASE_FIG));
2851+
if (shift == 0) {
2852+
VpAsgn(c.real, a.real, 1);
2853+
} else if (shiftDown) {
2854+
DECDIG carry = 0;
2855+
exponentShift++;
2856+
for (size_t i = 0; i < a.real->Prec; i++) {
2857+
DECDIG v = a.real->frac[i];
2858+
c.real->frac[i] = carry * ex + v / iex;
2859+
carry = v % iex;
2860+
}
2861+
c.real->frac[a.real->Prec] = carry * ex;
2862+
} else {
2863+
DECDIG carry = 0;
2864+
for (ssize_t i = a.real->Prec - 1; i >= 0; i--) {
2865+
DECDIG v = a.real->frac[i];
2866+
c.real->frac[i] = v % iex * ex + carry;
2867+
carry = v / iex;
2868+
}
2869+
}
2870+
while (c.real->frac[prec - 1] == 0) prec--;
2871+
c.real->Prec = prec;
2872+
c.real->sign = a.real->sign;
2873+
c.real->exponent = a.real->exponent;
2874+
AddExponent(c.real, exponentShift);
2875+
return CheckGetValue(c);
2876+
}
2877+
28192878
inline static int
28202879
is_zero(VALUE x)
28212880
{
@@ -3940,6 +3999,7 @@ Init_bigdecimal(void)
39403999
rb_define_method(rb_cBigDecimal, "infinite?", BigDecimal_IsInfinite, 0);
39414000
rb_define_method(rb_cBigDecimal, "finite?", BigDecimal_IsFinite, 0);
39424001
rb_define_method(rb_cBigDecimal, "truncate", BigDecimal_truncate, -1);
4002+
rb_define_method(rb_cBigDecimal, "_decimal_shift", BigDecimal_decimal_shift, 1);
39434003
rb_define_method(rb_cBigDecimal, "_dump", BigDecimal_dump, -1);
39444004

39454005
#ifdef BIGDECIMAL_USE_VP_TEST_METHODS
@@ -4000,7 +4060,6 @@ enum op_sw {
40004060
};
40014061

40024062
static int VpIsDefOP(Real *c, Real *a, Real *b, enum op_sw sw);
4003-
static int AddExponent(Real *a, SIGNED_VALUE n);
40044063
static DECDIG VpAddAbs(Real *a,Real *b,Real *c);
40054064
static DECDIG VpSubAbs(Real *a,Real *b,Real *c);
40064065
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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1375,6 +1375,24 @@ def test_sqrt_5266
13751375
x.sqrt(109).to_s(109).split(' ')[0])
13761376
end
13771377

1378+
def test_internal_use_decimal_shift
1379+
BigDecimal.mode(BigDecimal::EXCEPTION_INFINITY, false)
1380+
assert_positive_infinite(BigDecimal("Infinity")._decimal_shift(10))
1381+
assert_negative_infinite(BigDecimal("-Infinity")._decimal_shift(10))
1382+
assert_nan(BigDecimal::NAN._decimal_shift(10))
1383+
assert_equal(BigDecimal(0), BigDecimal(0)._decimal_shift(10))
1384+
[
1385+
BigDecimal('-1234.56789'),
1386+
BigDecimal('123456789.012345678987654321'),
1387+
BigDecimal('1234567890123.45678987654321')
1388+
].each do |num|
1389+
(0..20).each do |shift|
1390+
assert_equal(num * 10**shift, num._decimal_shift(shift))
1391+
assert_equal(num / 10**shift, num._decimal_shift(-shift))
1392+
end
1393+
end
1394+
end
1395+
13781396
def test_fix
13791397
x = BigDecimal("1.1")
13801398
assert_equal(1, x.fix)

0 commit comments

Comments
 (0)