5
5
*
6
6
*1998 Jan Wieck
7
7
*
8
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.25 2000/02/24 02:05:30 tgl Exp $
8
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/numeric.c,v 1.26 2000/03/13 02:31:13 tgl Exp $
9
9
*
10
10
* ----------
11
11
*/
@@ -491,14 +491,17 @@ numeric_sign(Numeric num)
491
491
/* ----------
492
492
* numeric_round() -
493
493
*
494
- *Modify rscale and dscale of a number and round it if required.
494
+ *Round a value to have 'scale' digits after the decimal point.
495
+ *We allow negative 'scale', implying rounding before the decimal
496
+ *point --- Oracle interprets rounding that way.
495
497
* ----------
496
498
*/
497
499
Numeric
498
500
numeric_round (Numeric num ,int32 scale )
499
501
{
500
- int32 typmod ;
501
- int precision ;
502
+ Numeric res ;
503
+ NumericVar arg ;
504
+ int i ;
502
505
503
506
/* ----------
504
507
* Handle NULL
@@ -515,27 +518,79 @@ numeric_round(Numeric num, int32 scale)
515
518
return make_result (& const_nan );
516
519
517
520
/* ----------
518
- *Check that therequested scaleis valid
521
+ *Limit the scalevalue to avoid possible overflow in calculations below.
519
522
* ----------
520
523
*/
521
- if (scale < 0 || scale > NUMERIC_MAX_DISPLAY_SCALE )
522
- elog (ERROR ,"illegal numeric scale %d - must be between 0 and %d" ,
523
- scale ,NUMERIC_MAX_DISPLAY_SCALE );
524
+ scale = MIN (NUMERIC_MAX_RESULT_SCALE ,
525
+ MAX (- NUMERIC_MAX_RESULT_SCALE ,scale ));
524
526
525
527
/* ----------
526
- *Let numeric() andin turn apply_typmod() do thejob
528
+ *Unpack the argument andround it at theproper digit position
527
529
* ----------
528
530
*/
529
- precision = MAX (0 ,num -> n_weight )+ scale ;
530
- typmod = (((precision + 2 ) <<16 ) |scale )+ VARHDRSZ ;
531
- return numeric (num ,typmod );
531
+ init_var (& arg );
532
+ set_var_from_num (num ,& arg );
533
+
534
+ i = arg .weight + scale + 1 ;
535
+
536
+ if (i < arg .ndigits )
537
+ {
538
+ /* If i = 0, the value loses all digits, but could round up if its
539
+ * first digit is more than 4. If i < 0 the result must be 0.
540
+ */
541
+ if (i < 0 )
542
+ {
543
+ arg .ndigits = 0 ;
544
+ }
545
+ else
546
+ {
547
+ int carry = (arg .digits [i ]> 4 ) ?1 :0 ;
548
+
549
+ arg .ndigits = i ;
550
+
551
+ while (carry )
552
+ {
553
+ carry += arg .digits [-- i ];
554
+ arg .digits [i ]= carry %10 ;
555
+ carry /=10 ;
556
+ }
557
+
558
+ if (i < 0 )
559
+ {
560
+ Assert (i == -1 );/* better not have added more than 1 digit */
561
+ Assert (arg .digits > arg .buf );
562
+ arg .digits -- ;
563
+ arg .ndigits ++ ;
564
+ arg .weight ++ ;
565
+ }
566
+ }
567
+ }
568
+
569
+ /* ----------
570
+ * Set result's scale to something reasonable.
571
+ * ----------
572
+ */
573
+ scale = MIN (NUMERIC_MAX_DISPLAY_SCALE ,MAX (0 ,scale ));
574
+ arg .rscale = scale ;
575
+ arg .dscale = scale ;
576
+
577
+ /* ----------
578
+ * Return the rounded result
579
+ * ----------
580
+ */
581
+ res = make_result (& arg );
582
+
583
+ free_var (& arg );
584
+ return res ;
532
585
}
533
586
534
587
535
588
/* ----------
536
589
* numeric_trunc() -
537
590
*
538
- *Modify rscale and dscale of a number and cut it if required.
591
+ *Truncate a value to have 'scale' digits after the decimal point.
592
+ *We allow negative 'scale', implying a truncation before the decimal
593
+ *point --- Oracle interprets truncation that way.
539
594
* ----------
540
595
*/
541
596
Numeric
@@ -559,25 +614,29 @@ numeric_trunc(Numeric num, int32 scale)
559
614
return make_result (& const_nan );
560
615
561
616
/* ----------
562
- *Check that therequested scaleis valid
617
+ *Limit the scalevalue to avoid possible overflow in calculations below.
563
618
* ----------
564
619
*/
565
- if (scale < 0 || scale > NUMERIC_MAX_DISPLAY_SCALE )
566
- elog (ERROR ,"illegal numeric scale %d - must be between 0 and %d" ,
567
- scale ,NUMERIC_MAX_DISPLAY_SCALE );
620
+ scale = MIN (NUMERIC_MAX_RESULT_SCALE ,
621
+ MAX (- NUMERIC_MAX_RESULT_SCALE ,scale ));
568
622
569
623
/* ----------
570
- * Unpack the argument and truncate it
624
+ * Unpack the argument and truncate it at the proper digit position
571
625
* ----------
572
626
*/
573
627
init_var (& arg );
574
628
set_var_from_num (num ,& arg );
575
629
630
+ arg .ndigits = MIN (arg .ndigits ,MAX (0 ,arg .weight + scale + 1 ));
631
+
632
+ /* ----------
633
+ * Set result's scale to something reasonable.
634
+ * ----------
635
+ */
636
+ scale = MIN (NUMERIC_MAX_DISPLAY_SCALE ,MAX (0 ,scale ));
576
637
arg .rscale = scale ;
577
638
arg .dscale = scale ;
578
639
579
- arg .ndigits = MIN (arg .ndigits ,MAX (0 ,arg .weight + scale + 1 ));
580
-
581
640
/* ----------
582
641
* Return the truncated result
583
642
* ----------