@@ -220,6 +220,7 @@ rbd_allocate_struct_one_nolimit(int sign, size_t const digits)
220
220
static unsigned short VpGetException (void );
221
221
static void VpSetException (unsigned short f );
222
222
static void VpCheckException (Real * p , bool always );
223
+ static int AddExponent (Real * a , SIGNED_VALUE n );
223
224
static VALUE CheckGetValue (BDVALUE v );
224
225
static void VpInternalRound (Real * c , size_t ixDigit , DECDIG vPrev , DECDIG v );
225
226
static int VpLimitRound (Real * c , size_t ixDigit );
@@ -2515,6 +2516,63 @@ BigDecimal_inspect(VALUE self)
2515
2516
return str ;
2516
2517
}
2517
2518
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
+
2518
2576
inline static int
2519
2577
is_zero (VALUE x )
2520
2578
{
@@ -3627,6 +3685,7 @@ Init_bigdecimal(void)
3627
3685
rb_define_method (rb_cBigDecimal , "infinite?" , BigDecimal_IsInfinite , 0 );
3628
3686
rb_define_method (rb_cBigDecimal , "finite?" , BigDecimal_IsFinite , 0 );
3629
3687
rb_define_method (rb_cBigDecimal , "truncate" , BigDecimal_truncate , -1 );
3688
+ rb_define_method (rb_cBigDecimal , "_decimal_shift" , BigDecimal_decimal_shift , 1 );
3630
3689
rb_define_method (rb_cBigDecimal , "_dump" , BigDecimal_dump , -1 );
3631
3690
3632
3691
#ifdef BIGDECIMAL_USE_VP_TEST_METHODS
@@ -3687,7 +3746,6 @@ enum op_sw {
3687
3746
};
3688
3747
3689
3748
static int VpIsDefOP (Real * c , Real * a , Real * b , enum op_sw sw );
3690
- static int AddExponent (Real * a , SIGNED_VALUE n );
3691
3749
static DECDIG VpAddAbs (Real * a ,Real * b ,Real * c );
3692
3750
static DECDIG VpSubAbs (Real * a ,Real * b ,Real * c );
3693
3751
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 );
0 commit comments