diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2003-01-09 01:07:18 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2003-01-09 01:07:18 +0000 |
commit | 9ecfdbcae12547bfb24d2387d70e24072aab6849 (patch) | |
tree | b3512d909fe6a353baee9d3c6f26d4928f4bf746 /src | |
parent | 9260e79d11935a6a5f9255eb998625ca38b9b0c7 (diff) | |
download | postgresql-9ecfdbcae12547bfb24d2387d70e24072aab6849.tar.gz postgresql-9ecfdbcae12547bfb24d2387d70e24072aab6849.zip |
Repair inconsistent rounding behavior for timestamp, time, interval,
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/utils/adt/date.c | 57 | ||||
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 104 |
2 files changed, 56 insertions, 105 deletions
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index a35353f450d..7f15784320c 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.73.2.1 2002/11/21 23:31:37 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.73.2.2 2003/01/09 01:07:17 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; } diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index 2fe99308c05..046d86c92f8 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.74.2.1 2002/11/12 00:39:36 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.74.2.2 2003/01/09 01:07:18 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 } } |