@@ -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 );
@@ -2514,6 +2515,63 @@ BigDecimal_inspect(VALUE self)
2514
2515
return str ;
2515
2516
}
2516
2517
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
+
2517
2575
inline static int
2518
2576
is_zero (VALUE x )
2519
2577
{
@@ -3626,6 +3684,7 @@ Init_bigdecimal(void)
3626
3684
rb_define_method (rb_cBigDecimal , "infinite?" , BigDecimal_IsInfinite , 0 );
3627
3685
rb_define_method (rb_cBigDecimal , "finite?" , BigDecimal_IsFinite , 0 );
3628
3686
rb_define_method (rb_cBigDecimal , "truncate" , BigDecimal_truncate , -1 );
3687
+ rb_define_method (rb_cBigDecimal , "_decimal_shift" , BigDecimal_decimal_shift , 1 );
3629
3688
rb_define_method (rb_cBigDecimal , "_dump" , BigDecimal_dump , -1 );
3630
3689
3631
3690
#ifdef BIGDECIMAL_USE_VP_TEST_METHODS
@@ -3686,7 +3745,6 @@ enum op_sw {
3686
3745
};
3687
3746
3688
3747
static int VpIsDefOP (Real * c , Real * a , Real * b , enum op_sw sw );
3689
- static int AddExponent (Real * a , SIGNED_VALUE n );
3690
3748
static DECDIG VpAddAbs (Real * a ,Real * b ,Real * c );
3691
3749
static DECDIG VpSubAbs (Real * a ,Real * b ,Real * c );
3692
3750
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