Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit fb715e0

Browse files
committed
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.
1 parent 2cd00f0 commit fb715e0

File tree

2 files changed

+56
-105
lines changed

2 files changed

+56
-105
lines changed

src/backend/utils/adt/date.c

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.74 2002/11/21 23:31:20 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/utils/adt/date.c,v 1.75 2003/01/09 01:06:57 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -630,12 +630,12 @@ AdjustTimeForTypmod(TimeADT *time, int32 typmod)
630630
};
631631

632632
static const int64 TimeOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
633-
INT64CONST(-500000),
634-
INT64CONST(-50000),
635-
INT64CONST(-5000),
636-
INT64CONST(-500),
637-
INT64CONST(-50),
638-
INT64CONST(-5),
633+
INT64CONST(500000),
634+
INT64CONST(50000),
635+
INT64CONST(5000),
636+
INT64CONST(500),
637+
INT64CONST(50),
638+
INT64CONST(5),
639639
INT64CONST(0)
640640
};
641641

@@ -649,52 +649,33 @@ AdjustTimeForTypmod(TimeADT *time, int32 typmod)
649649
100000,
650650
1000000
651651
};
652-
653-
static const double TimeOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
654-
0.5,
655-
0.05,
656-
0.005,
657-
0.0005,
658-
0.00005,
659-
0.000005,
660-
0.0000005
661-
};
662652
#endif
663653

664654
if ((typmod >= 0) && (typmod <= MAX_TIME_PRECISION))
665655
{
656+
/*
657+
* Note: this round-to-nearest code is not completely consistent
658+
* about rounding values that are exactly halfway between integral
659+
* values. On most platforms, rint() will implement round-to-nearest,
660+
* but the integer code always rounds up (away from zero). Is it
661+
* worth trying to be consistent?
662+
*/
666663
#ifdef HAVE_INT64_TIMESTAMP
667-
/* we have different truncation behavior depending on sign */
668664
if (*time >= INT64CONST(0))
669-
{
670-
*time = ((*time / TimeScales[typmod])
671-
* TimeScales[typmod]);
672-
}
673-
else
674665
{
675666
*time = (((*time + TimeOffsets[typmod]) / TimeScales[typmod])
676667
* TimeScales[typmod]);
677668
}
678-
#else
679-
/* we have different truncation behavior depending on sign */
680-
if (*time >= 0)
681-
{
682-
*time = (rint(((double) *time) * TimeScales[typmod])
683-
/ TimeScales[typmod]);
684-
}
685669
else
686670
{
687-
/*
688-
* Scale and truncate first, then add to help the rounding
689-
* behavior
690-
*/
691-
*time = (rint((((double) *time) * TimeScales[typmod]) + TimeOffsets[typmod])
692-
/ TimeScales[typmod]);
671+
*time = - ((((- *time) + TimeOffsets[typmod]) / TimeScales[typmod])
672+
* TimeScales[typmod]);
693673
}
674+
#else
675+
*time = (rint(((double) *time) * TimeScales[typmod])
676+
/ TimeScales[typmod]);
694677
#endif
695678
}
696-
697-
return;
698679
}
699680

700681

src/backend/utils/adt/timestamp.c

Lines changed: 37 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.75 2002/11/12 00:39:08 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v 1.76 2003/01/09 01:06:57 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -175,12 +175,12 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
175175
};
176176

177177
static const int64 TimestampOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
178-
INT64CONST(-500000),
179-
INT64CONST(-50000),
180-
INT64CONST(-5000),
181-
INT64CONST(-500),
182-
INT64CONST(-50),
183-
INT64CONST(-5),
178+
INT64CONST(500000),
179+
INT64CONST(50000),
180+
INT64CONST(5000),
181+
INT64CONST(500),
182+
INT64CONST(50),
183+
INT64CONST(5),
184184
INT64CONST(0)
185185
};
186186

@@ -194,16 +194,6 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
194194
100000,
195195
1000000
196196
};
197-
198-
static const double TimestampOffsets[MAX_TIMESTAMP_PRECISION + 1] = {
199-
0.5,
200-
0.05,
201-
0.005,
202-
0.0005,
203-
0.00005,
204-
0.000005,
205-
0.0000005
206-
};
207197
#endif
208198

209199
if (!TIMESTAMP_NOT_FINITE(*time)
@@ -213,34 +203,27 @@ AdjustTimestampForTypmod(Timestamp *time, int32 typmod)
213203
elog(ERROR, "TIMESTAMP(%d) precision must be between %d and %d",
214204
typmod, 0, MAX_TIMESTAMP_PRECISION);
215205

206+
/*
207+
* Note: this round-to-nearest code is not completely consistent
208+
* about rounding values that are exactly halfway between integral
209+
* values. On most platforms, rint() will implement round-to-nearest,
210+
* but the integer code always rounds up (away from zero). Is it
211+
* worth trying to be consistent?
212+
*/
216213
#ifdef HAVE_INT64_TIMESTAMP
217-
/* we have different truncation behavior depending on sign */
218214
if (*time >= INT64CONST(0))
219-
{
220-
*time = ((*time / TimestampScales[typmod])
221-
* TimestampScales[typmod]);
222-
}
223-
else
224215
{
225216
*time = (((*time + TimestampOffsets[typmod]) / TimestampScales[typmod])
226217
* TimestampScales[typmod]);
227218
}
228-
#else
229-
/* we have different truncation behavior depending on sign */
230-
if (*time >= 0)
231-
{
232-
*time = (rint(((double) *time) * TimestampScales[typmod])
233-
/ TimestampScales[typmod]);
234-
}
235219
else
236220
{
237-
/*
238-
* Scale and truncate first, then add to help the rounding
239-
* behavior
240-
*/
241-
*time = (rint((((double) *time) * TimestampScales[typmod]) + TimestampOffsets[typmod])
242-
/ TimestampScales[typmod]);
221+
*time = - ((((- *time) + TimestampOffsets[typmod]) / TimestampScales[typmod])
222+
* TimestampScales[typmod]);
243223
}
224+
#else
225+
*time = (rint(((double) *time) * TimestampScales[typmod])
226+
/ TimestampScales[typmod]);
244227
#endif
245228
}
246229
}
@@ -474,12 +457,12 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
474457
};
475458

476459
static const int64 IntervalOffsets[MAX_INTERVAL_PRECISION + 1] = {
477-
INT64CONST(-500000),
478-
INT64CONST(-50000),
479-
INT64CONST(-5000),
480-
INT64CONST(-500),
481-
INT64CONST(-50),
482-
INT64CONST(-5),
460+
INT64CONST(500000),
461+
INT64CONST(50000),
462+
INT64CONST(5000),
463+
INT64CONST(500),
464+
INT64CONST(50),
465+
INT64CONST(5),
483466
INT64CONST(0)
484467
};
485468

@@ -493,16 +476,6 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
493476
100000,
494477
1000000
495478
};
496-
497-
static const double IntervalOffsets[MAX_INTERVAL_PRECISION + 1] = {
498-
0.5,
499-
0.05,
500-
0.005,
501-
0.0005,
502-
0.00005,
503-
0.000005,
504-
0.0000005
505-
};
506479
#endif
507480

508481
/*
@@ -701,30 +674,27 @@ AdjustIntervalForTypmod(Interval *interval, int32 typmod)
701674
elog(ERROR, "INTERVAL(%d) precision must be between %d and %d",
702675
precision, 0, MAX_INTERVAL_PRECISION);
703676

677+
/*
678+
* Note: this round-to-nearest code is not completely consistent
679+
* about rounding values that are exactly halfway between integral
680+
* values. On most platforms, rint() will implement round-to-nearest,
681+
* but the integer code always rounds up (away from zero). Is it
682+
* worth trying to be consistent?
683+
*/
704684
#ifdef HAVE_INT64_TIMESTAMP
705-
/* we have different truncation behavior depending on sign */
706685
if (interval->time >= INT64CONST(0))
707-
{
708-
interval->time = ((interval->time / IntervalScales[precision])
709-
* IntervalScales[precision]);
710-
}
711-
else
712686
{
713687
interval->time = (((interval->time + IntervalOffsets[precision]) / IntervalScales[precision])
714688
* IntervalScales[precision]);
715689
}
716-
#else
717-
/* we have different truncation behavior depending on sign */
718-
if (interval->time >= 0)
719-
{
720-
interval->time = (rint(((double) interval->time) * IntervalScales[precision])
721-
/ IntervalScales[precision]);
722-
}
723690
else
724691
{
725-
interval->time = (rint((((double) interval->time) + IntervalOffsets[precision])
726-
* IntervalScales[precision]) / IntervalScales[precision]);
692+
interval->time = - (((-interval->time + IntervalOffsets[precision]) / IntervalScales[precision])
693+
* IntervalScales[precision]);
727694
}
695+
#else
696+
interval->time = (rint(((double) interval->time) * IntervalScales[precision])
697+
/ IntervalScales[precision]);
728698
#endif
729699
}
730700
}

0 commit comments

Comments
 (0)