]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Repair inconsistent rounding behavior for timestamp, time, interval,
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 9 Jan 2003 01:06:57 +0000 (01:06 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 9 Jan 2003 01:06:57 +0000 (01:06 +0000)
per gripe from Csaba Nagy.  There is still potential for platform-specific
behavior for values that are exactly halfway between integers, but at
least we now get the expected answer for all other cases.

src/backend/utils/adt/date.c
src/backend/utils/adt/timestamp.c

index 3b921258890d14fdef817f2d306e18be2162d661..b952480f2fe303991b397203fcc2e89be6b145a3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.74 2002/11/21 23:31:20 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.75 2003/01/09 01:06:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -630,12 +630,12 @@ AdjustTimeForTypmod(TimeADT *time, int32 typmod)
        };
 
        static const int64 TimeOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
-               INT64CONST(-500000),
-               INT64CONST(-50000),
-               INT64CONST(-5000),
-               INT64CONST(-500),
-               INT64CONST(-50),
-               INT64CONST(-5),
+               INT64CONST(500000),
+               INT64CONST(50000),
+               INT64CONST(5000),
+               INT64CONST(500),
+               INT64CONST(50),
+               INT64CONST(5),
                INT64CONST(0)
        };
 
@@ -649,52 +649,33 @@ AdjustTimeForTypmod(TimeADT *time, int32 typmod)
                100000,
                1000000
        };
-
-       static const double TimeOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
-               0.5,
-               0.05,
-               0.005,
-               0.0005,
-               0.00005,
-               0.000005,
-               0.0000005
-       };
 #endif
 
        if ((typmod >= 0) && (typmod <= MAX_TIME_PRECISION))
        {
+               /*
+                * Note: this round-to-nearest code is not completely consistent
+                * about rounding values that are exactly halfway between integral
+                * values.  On most platforms, rint() will implement round-to-nearest,
+                * but the integer code always rounds up (away from zero).  Is it
+                * worth trying to be consistent?
+                */
 #ifdef HAVE_INT64_TIMESTAMP
-               /* we have different truncation behavior depending on sign */
                if (*time >= INT64CONST(0))
-               {
-                       *time = ((*time / TimeScales[typmod])
-                                        * TimeScales[typmod]);
-               }
-               else
                {
                        *time = (((*time + TimeOffsets[typmod]) / TimeScales[typmod])
                                         * TimeScales[typmod]);
                }
-#else
-               /* we have different truncation behavior depending on sign */
-               if (*time >= 0)
-               {
-                       *time = (rint(((double) *time) * TimeScales[typmod])
-                                        / TimeScales[typmod]);
-               }
                else
                {
-                       /*
-                        * Scale and truncate first, then add to help the rounding
-                        * behavior
-                        */
-                       *time = (rint((((double) *time) * TimeScales[typmod]) + TimeOffsets[typmod])
-                                        / TimeScales[typmod]);
+                       *time = - ((((- *time) + TimeOffsets[typmod]) / TimeScales[typmod])
+                                          * TimeScales[typmod]);
                }
+#else
+               *time = (rint(((double) *time) * TimeScales[typmod])
+                                / TimeScales[typmod]);
 #endif
        }
-
-       return;
 }
 
 
index 885d3992fd44ab5304e7845ab23a89e9507b1a14..19fb9c118438135748919e75ba281b9f7d644ab8 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.75 2002/11/12 00:39:08 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.76 2003/01/09 01:06:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -175,12 +175,12 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
        };
 
        static const int64 TimestampOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
-               INT64CONST(-500000),
-               INT64CONST(-50000),
-               INT64CONST(-5000),
-               INT64CONST(-500),
-               INT64CONST(-50),
-               INT64CONST(-5),
+               INT64CONST(500000),
+               INT64CONST(50000),
+               INT64CONST(5000),
+               INT64CONST(500),
+               INT64CONST(50),
+               INT64CONST(5),
                INT64CONST(0)
        };
 
@@ -194,16 +194,6 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
                100000,
                1000000
        };
-
-       static const double TimestampOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
-               0.5,
-               0.05,
-               0.005,
-               0.0005,
-               0.00005,
-               0.000005,
-               0.0000005
-       };
 #endif
 
        if (!TIMESTAMP_NOT_FINITE(*time)
@@ -213,34 +203,27 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
                        elog(ERROR, "TIMESTAMP(%d) precision must be between %d and %d",
                                 typmod, 0, MAX_TIMESTAMP_PRECISION);
 
+               /*
+                * Note: this round-to-nearest code is not completely consistent
+                * about rounding values that are exactly halfway between integral
+                * values.  On most platforms, rint() will implement round-to-nearest,
+                * but the integer code always rounds up (away from zero).  Is it
+                * worth trying to be consistent?
+                */
 #ifdef HAVE_INT64_TIMESTAMP
-               /* we have different truncation behavior depending on sign */
                if (*time >= INT64CONST(0))
-               {
-                       *time = ((*time / TimestampScales[typmod])
-                                        * TimestampScales[typmod]);
-               }
-               else
                {
                        *time = (((*time + TimestampOffsets[typmod]) / TimestampScales[typmod])
                                         * TimestampScales[typmod]);
                }
-#else
-               /* we have different truncation behavior depending on sign */
-               if (*time >= 0)
-               {
-                       *time = (rint(((double) *time) * TimestampScales[typmod])
-                                        / TimestampScales[typmod]);
-               }
                else
                {
-                       /*
-                        * Scale and truncate first, then add to help the rounding
-                        * behavior
-                        */
-                       *time = (rint((((double) *time) * TimestampScales[typmod]) + TimestampOffsets[typmod])
-                                        / TimestampScales[typmod]);
+                       *time = - ((((- *time) + TimestampOffsets[typmod]) / TimestampScales[typmod])
+                                          * TimestampScales[typmod]);
                }
+#else
+               *time = (rint(((double) *time) * TimestampScales[typmod])
+                                / TimestampScales[typmod]);
 #endif
        }
 }
@@ -474,12 +457,12 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
        };
 
        static const int64 IntervalOffsets[MAX_INTERVAL_PRECISION + 1] = {
-               INT64CONST(-500000),
-               INT64CONST(-50000),
-               INT64CONST(-5000),
-               INT64CONST(-500),
-               INT64CONST(-50),
-               INT64CONST(-5),
+               INT64CONST(500000),
+               INT64CONST(50000),
+               INT64CONST(5000),
+               INT64CONST(500),
+               INT64CONST(50),
+               INT64CONST(5),
                INT64CONST(0)
        };
 
@@ -493,16 +476,6 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
                100000,
                1000000
        };
-
-       static const double IntervalOffsets[MAX_INTERVAL_PRECISION + 1] = {
-               0.5,
-               0.05,
-               0.005,
-               0.0005,
-               0.00005,
-               0.000005,
-               0.0000005
-       };
 #endif
 
        /*
@@ -701,30 +674,27 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
                                elog(ERROR, "INTERVAL(%d) precision must be between %d and %d",
                                         precision, 0, MAX_INTERVAL_PRECISION);
 
+                       /*
+                        * Note: this round-to-nearest code is not completely consistent
+                        * about rounding values that are exactly halfway between integral
+                        * values.  On most platforms, rint() will implement round-to-nearest,
+                        * but the integer code always rounds up (away from zero).  Is it
+                        * worth trying to be consistent?
+                        */
 #ifdef HAVE_INT64_TIMESTAMP
-                       /* we have different truncation behavior depending on sign */
                        if (interval->time >= INT64CONST(0))
-                       {
-                               interval->time = ((interval->time / IntervalScales[precision])
-                                                                 * IntervalScales[precision]);
-                       }
-                       else
                        {
                                interval->time = (((interval->time + IntervalOffsets[precision]) / IntervalScales[precision])
                                                                  * IntervalScales[precision]);
                        }
-#else
-                       /* we have different truncation behavior depending on sign */
-                       if (interval->time >= 0)
-                       {
-                               interval->time = (rint(((double) interval->time) * IntervalScales[precision])
-                                                                 / IntervalScales[precision]);
-                       }
                        else
                        {
-                               interval->time = (rint((((double) interval->time) + IntervalOffsets[precision])
-                               * IntervalScales[precision]) / IntervalScales[precision]);
+                               interval->time = - (((-interval->time + IntervalOffsets[precision]) / IntervalScales[precision])
+                                                                       * IntervalScales[precision]);
                        }
+#else
+                       interval->time = (rint(((double) interval->time) * IntervalScales[precision])
+                                                         / IntervalScales[precision]);
 #endif
                }
        }