@@ -319,110 +319,192 @@ BigDecimal_prec(VALUE self)
319319 return obj ;
320320}
321321
322- /*
323- * call-seq:
324- * precision -> integer
325- *
326- * Returns the number of decimal digits in +self+:
327- *
328- * BigDecimal("0").precision # => 0
329- * BigDecimal("1").precision # => 1
330- * BigDecimal("-1e20").precision # => 21
331- * BigDecimal("1e-20").precision # => 20
332- * BigDecimal("Infinity").precision # => 0
333- * BigDecimal("-Infinity").precision # => 0
334- * BigDecimal("NaN").precision # => 0
335- *
336- */
337- static VALUE
338- BigDecimal_precision (VALUE self )
322+ static void
323+ BigDecimal_count_precision_and_scale (VALUE self , ssize_t * out_precision , ssize_t * out_scale )
339324{
340325 ENTER (1 );
341326
327+ if (out_precision == NULL && out_scale == NULL )
328+ return ;
329+
342330 Real * p ;
343331 GUARD_OBJ (p , GetVpValue (self , 1 ));
344- if (VpIsZero (p ) || !VpIsDef (p )) return INT2FIX (0 );
332+ if (VpIsZero (p ) || !VpIsDef (p )) {
333+ zero :
334+ if (out_precision ) * out_precision = 0 ;
335+ if (out_scale ) * out_scale = 0 ;
336+ return ;
337+ }
338+
339+ DECDIG x ;
340+
341+ ssize_t n = p -> Prec ; /* The length of frac without zeros. */
342+ while (n > 0 && p -> frac [n - 1 ] == 0 ) -- n ;
343+ if (n == 0 ) goto zero ;
344+
345+ int nlz = BASE_FIG ;
346+ for (x = p -> frac [0 ]; x > 0 ; x /= 10 ) -- nlz ;
347+
348+ int ntz = 0 ;
349+ for (x = p -> frac [n - 1 ]; x > 0 && x % 10 == 0 ; x /= 10 ) ++ ntz ;
345350
346351 /*
347- * The most significant digit is frac[0], and the least significant digit is frac[Prec-1].
348- * When the exponent is zero, the decimal point is located just before frac[0].
352+ * Calculate the precision and the scale
353+ * -------------------------------------
349354 *
355+ * The most significant digit is frac[0], and the least significant digit
356+ * is frac[Prec-1]. When the exponent is zero, the decimal point is
357+ * located just before frac[0].
350358 *
351359 * When the exponent is negative, the decimal point moves to leftward.
352- * In this case, the precision can be calculated by BASE_FIG * (-exponent + Prec) - ntz.
360+ * In this case, the precision can be calculated by
361+ *
362+ * precision = BASE_FIG * (-exponent + n) - ntz,
363+ *
364+ * and the scale is the same as precision.
353365 *
354- * 0 . 0000 0000 | frac[0] frac[1] ... frac[Prec-1]
355- * <----------| exponent == -2
366+ * 0 . 0000 0000 | frac[0] ... frac[n-1] |
367+ * |<----------| exponent == -2 |
368+ * |---------------------------------->| precision
369+ * |---------------------------------->| scale
356370 *
357- * Conversely, when the exponent is positive, the decimal point moves to rightward.
358371 *
359- * | frac[0] frac[1] frac[2] . frac[3] frac[4] ... frac[Prec-1]
360- * |------------------------> exponent == 3
372+ * Conversely, when the exponent is positive, the decimal point moves to
373+ * rightward. In this case, the scale equals to
374+ *
375+ * BASE_FIG * (n - exponent) - ntz.
376+ *
377+ * the precision equals to
378+ *
379+ * scale + BASE_FIG * exponent - nlz.
380+ *
381+ * | frac[0] frac[1] . frac[2] ... frac[n-1] |
382+ * |---------------->| exponent == 2 |
383+ * | |---------------------->| scale
384+ * |---------------------------------------->| precision
361385 */
362386
363387 ssize_t ex = p -> exponent ;
364388
365389 /* Count the number of decimal digits before frac[1]. */
366- ssize_t precision = BASE_FIG ; /* The number of decimal digits in frac[0]. */
390+ ssize_t n_digits_head = BASE_FIG ;
367391 if (ex < 0 ) {
368- precision += - ex * BASE_FIG ; /* The number of leading zeros before frac[0]. */
369- ex = 0 ;
392+ n_digits_head += ( - ex ) * BASE_FIG ; /* The number of leading zeros before frac[0]. */
393+ ex = 0 ;
370394 }
371395 else if (ex > 0 ) {
372- /* Count the number of decimal digits without the leading zeros in
373- * the most significant digit in the integral part. */
374- DECDIG x = p -> frac [0 ];
375- for (precision = 0 ; x > 0 ; x /= 10 ) {
376- ++ precision ;
377- }
396+ /* Count the number of decimal digits without the leading zeros in
397+ * the most significant digit in the integral part.
398+ */
399+ n_digits_head -= nlz ; /* Make the number of digits */
378400 }
379401
380- /* Count the number of decimal digits after frac[0]. */
381- if (ex > (ssize_t )p -> Prec ) {
382- /* In this case the number is an integer with multiple trailing zeros. */
383- precision += (ex - 1 ) * BASE_FIG ;
402+ if (out_precision ) {
403+ ssize_t precision = n_digits_head ;
404+
405+ /* Count the number of decimal digits after frac[0]. */
406+ if (ex > (ssize_t )n ) {
407+ /* In this case the number is an integer with some trailing zeros. */
408+ precision += (ex - 1 ) * BASE_FIG ;
409+ }
410+ else if (n > 0 ) {
411+ precision += (n - 1 ) * BASE_FIG ;
412+
413+ if (ex < (ssize_t )n ) {
414+ precision -= ntz ;
415+ }
416+ }
417+
418+ * out_precision = precision ;
384419 }
385- else if (p -> Prec > 0 ) {
386- ssize_t n = (ssize_t )p -> Prec - 1 ;
387- while (n > 0 && p -> frac [n ] == 0 ) -- n ; /* Skip trailing zeros, just in case. */
388420
389- precision += n * BASE_FIG ;
421+ if (out_scale ) {
422+ ssize_t scale = 0 ;
390423
391- if (ex < ( ssize_t ) p -> Prec ) {
392- DECDIG x = p -> frac [ n ] ;
393- for (; x > 0 && x % 10 == 0 ; x /= 10 ) {
394- -- precision ;
395- }
424+ if (p -> exponent < 0 ) {
425+ scale = n_digits_head + ( n - 1 ) * BASE_FIG - ntz ;
426+ }
427+ else if ( n > p -> exponent ) {
428+ scale = ( n - p -> exponent ) * BASE_FIG - ntz ;
396429 }
430+
431+ * out_scale = scale ;
397432 }
433+ }
398434
435+ /*
436+ * call-seq:
437+ * precision -> integer
438+ *
439+ * Returns the number of decimal digits in +self+:
440+ *
441+ * BigDecimal("0").precision # => 0
442+ * BigDecimal("1").precision # => 1
443+ * BigDecimal("1.1").precision # => 2
444+ * BigDecimal("3.1415").precision # => 5
445+ * BigDecimal("-1e20").precision # => 21
446+ * BigDecimal("1e-20").precision # => 20
447+ * BigDecimal("Infinity").precision # => 0
448+ * BigDecimal("-Infinity").precision # => 0
449+ * BigDecimal("NaN").precision # => 0
450+ *
451+ */
452+ static VALUE
453+ BigDecimal_precision (VALUE self )
454+ {
455+ ssize_t precision ;
456+ BigDecimal_count_precision_and_scale (self , & precision , NULL );
399457 return SSIZET2NUM (precision );
400458}
401459
460+ /*
461+ * call-seq:
462+ * scale -> integer
463+ *
464+ * Returns the number of decimal digits following the decimal digits in +self+.
465+ *
466+ * BigDecimal("0").scale # => 0
467+ * BigDecimal("1").scale # => 1
468+ * BigDecimal("1.1").scale # => 1
469+ * BigDecimal("3.1415").scale # => 4
470+ * BigDecimal("-1e20").precision # => 0
471+ * BigDecimal("1e-20").precision # => 20
472+ * BigDecimal("Infinity").scale # => 0
473+ * BigDecimal("-Infinity").scale # => 0
474+ * BigDecimal("NaN").scale # => 0
475+ */
476+ static VALUE
477+ BigDecimal_scale (VALUE self )
478+ {
479+ ssize_t scale ;
480+ BigDecimal_count_precision_and_scale (self , NULL , & scale );
481+ return SSIZET2NUM (scale );
482+ }
483+
402484static VALUE
403485BigDecimal_n_significant_digits (VALUE self )
404486{
405487 ENTER (1 );
406488
407489 Real * p ;
408490 GUARD_OBJ (p , GetVpValue (self , 1 ));
409-
410- ssize_t n = p -> Prec ;
411- while (n > 0 && p -> frac [n - 1 ] == 0 ) -- n ;
412- if (n <= 0 ) {
491+ if (VpIsZero (p ) || !VpIsDef (p )) {
413492 return INT2FIX (0 );
414493 }
415494
416- int nlz , ntz ;
495+ ssize_t n = p -> Prec ; /* The length of frac without trailing zeros. */
496+ for (n = p -> Prec ; n > 0 && p -> frac [n - 1 ] == 0 ; -- n );
497+ if (n == 0 ) return INT2FIX (0 );
417498
418- DECDIG x = p -> frac [0 ];
419- for (nlz = BASE_FIG ; x > 0 ; x /= 10 ) -- nlz ;
499+ DECDIG x ;
500+ int nlz = BASE_FIG ;
501+ for (x = p -> frac [0 ]; x > 0 ; x /= 10 ) -- nlz ;
420502
421- x = p -> frac [ n - 1 ] ;
422- for (ntz = 0 ; x > 0 && x % 10 == 0 ; x /= 10 ) ++ ntz ;
503+ int ntz = 0 ;
504+ for (x = p -> frac [ n - 1 ] ; x > 0 && x % 10 == 0 ; x /= 10 ) ++ ntz ;
423505
424- ssize_t n_digits = BASE_FIG * n - nlz - ntz ;
425- return SSIZET2NUM (n_digits );
506+ ssize_t n_significant_digits = BASE_FIG * n - nlz - ntz ;
507+ return SSIZET2NUM (n_significant_digits );
426508}
427509
428510/*
@@ -4129,6 +4211,7 @@ Init_bigdecimal(void)
41294211 /* instance methods */
41304212 rb_define_method (rb_cBigDecimal , "precs" , BigDecimal_prec , 0 );
41314213 rb_define_method (rb_cBigDecimal , "precision" , BigDecimal_precision , 0 );
4214+ rb_define_method (rb_cBigDecimal , "scale" , BigDecimal_scale , 0 );
41324215 rb_define_method (rb_cBigDecimal , "n_significant_digits" , BigDecimal_n_significant_digits , 0 );
41334216
41344217 rb_define_method (rb_cBigDecimal , "add" , BigDecimal_add2 , 2 );
0 commit comments