@@ -302,6 +302,7 @@ rbd_allocate_struct_one_nolimit(int sign, size_t const digits)
302
302
static unsigned short VpGetException (void );
303
303
static void VpSetException (unsigned short f );
304
304
static void VpCheckException (Real * p , bool always );
305
+ static int AddExponent (Real * a , SIGNED_VALUE n );
305
306
static VALUE CheckGetValue (BDVALUE v );
306
307
static void VpInternalRound (Real * c , size_t ixDigit , DECDIG vPrev , DECDIG v );
307
308
static int VpLimitRound (Real * c , size_t ixDigit );
@@ -2816,6 +2817,64 @@ BigDecimal_inspect(VALUE self)
2816
2817
return str ;
2817
2818
}
2818
2819
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
+
2819
2878
inline static int
2820
2879
is_zero (VALUE x )
2821
2880
{
@@ -3940,6 +3999,7 @@ Init_bigdecimal(void)
3940
3999
rb_define_method (rb_cBigDecimal , "infinite?" , BigDecimal_IsInfinite , 0 );
3941
4000
rb_define_method (rb_cBigDecimal , "finite?" , BigDecimal_IsFinite , 0 );
3942
4001
rb_define_method (rb_cBigDecimal , "truncate" , BigDecimal_truncate , -1 );
4002
+ rb_define_method (rb_cBigDecimal , "_decimal_shift" , BigDecimal_decimal_shift , 1 );
3943
4003
rb_define_method (rb_cBigDecimal , "_dump" , BigDecimal_dump , -1 );
3944
4004
3945
4005
#ifdef BIGDECIMAL_USE_VP_TEST_METHODS
@@ -4000,7 +4060,6 @@ enum op_sw {
4000
4060
};
4001
4061
4002
4062
static int VpIsDefOP (Real * c , Real * a , Real * b , enum op_sw sw );
4003
- static int AddExponent (Real * a , SIGNED_VALUE n );
4004
4063
static DECDIG VpAddAbs (Real * a ,Real * b ,Real * c );
4005
4064
static DECDIG VpSubAbs (Real * a ,Real * b ,Real * c );
4006
4065
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