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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy