Michael Glaesemann wrote:
> On Aug 30, 2006, at 7:12 , Bruce Momjian wrote:
> 
> > Here are the results using my newest patch:
> >
> >     test=> select interval '41 mon 12 days 360:00' / 10 as quotient_a
> >          , interval '41 mon -12 days -360:00' / 10 as quotient_b
> >          , interval '-41 mon 12 days 360:00' / 10 as quotient_c
> >          , interval '-41 mon -12 days -360:00' / 10 as quotient_d;
> >            quotient_a       |       quotient_b        |         
> > quotient_c         |        quotient_d
> >     ------------------------+------------------------- 
> > +---------------------------+---------------------------
> >      4 mons 4 days 40:48:00 | 4 mons 2 days -40:48:00 | -4 mons -2  
> > days +40:48:00 | -4 mons -4 days -40:48:00
> >     (1 row)
> >     
> >     test=> select interval '41 mon 12 days 360:00' * 0.3 as product_a
> >          , interval '41 mon -12 days -360:00' * 0.3 as product_b
> >          , interval '-41 mon 12 days 360:00' * 0.3 as product_c
> >          , interval '-41 mon -12 days -360:00' * 0.3 as product_d;
> >             product_a         |        product_b         |           
> > product_c          |          product_d
> >     --------------------------+-------------------------- 
> > +-----------------------------+------------------------------
> >      1 year 12 days 122:24:00 | 1 year 6 days -122:24:00 | -1 years -6  
> > days +122:24:00 | -1 years -12 days -122:24:00
> >     (1 row)
> >
> > I see no "23:60" entries.
> 
> Using Bruce's newest patch, I still get the "23:60" entries on my  
> machine (no integer-datetimes)


Strange, I do not see that here.  Is there something wrong with our
hour/minute display?  Someone posted a patch a few days ago for that.

Here is a test program.  What does it show for you?

        #include <stdio.h>
        
        
        int
        main(int argc, char *argv[])
        {
                double x;
        
                x = 41;
                x = x / 10.0;
                printf("%f\n", x);
                x = x - (int)x;
                x = x * 30;
                printf("%15.15f\n", x);
                x = 0.1 * 30;
                printf("%15.15f\n", x);
                return 0;
        }
        
The output for me is:

        4.100000000000000
        2.999999999999989
        3.000000000000000


> 
> select version();
>                                                                        
> version
> ------------------------------------------------------------------------ 
> ------------------------------------------------------------------------ 
> -
> PostgreSQL 8.2devel on powerpc-apple-darwin8.7.0, compiled by GCC  
> powerpc-apple-darwin8-gcc-4.0.1 (GCC) 4.0.1 (Apple Computer, Inc.  
> build 5341)
> (1 row)

Powerpc.  Hmmm.  I am on Intel.

> select interval '41 mon 12 days 360:00' / 10 as quotient_a
>      , interval '41 mon -12 days -360:00' / 10 as quotient_b
>      , interval '-41 mon 12 days 360:00' / 10 as quotient_c
>      , interval '-41 mon -12 days -360:00' / 10 as quotient_d;
>         quotient_a       |       quotient_b        |         
> quotient_c         |        quotient_d
> ------------------------+------------------------- 
> +---------------------------+---------------------------
> 4 mons 4 days 40:48:00 | 4 mons 2 days -40:48:00 | -4 mons -2 days  
> +40:48:00 | -4 mons -4 days -40:48:00
> (1 row)
> 
> select interval '41 mon 12 days 360:00' * 0.3 as product_a
>      , interval '41 mon -12 days -360:00' * 0.3 as product_b
>      , interval '-41 mon 12 days 360:00' * 0.3 as product_c
>      , interval '-41 mon -12 days -360:00' * 0.3 as product_d;
>          product_a         |          product_b          |           
> product_c          |            product_d
> --------------------------+----------------------------- 
> +-----------------------------+---------------------------------
> 1 year 12 days 122:24:00 | 1 year 6 days -122:23:60.00 | -1 years -6  
> days +122:24:00 | -1 years -12 days -122:23:60.00
> (1 row)
> 

Yea, I see that -122:23:60.00.

> > The code assume if it is within 0.000001 of a whole number, it  
> > should be
> > rounded to a whole number. Patch attached with comments added.
> 
> >     /* fractional months full days into days */
> >     month_remainder_days = month_remainder * DAYS_PER_MONTH;
> > +   /*
> > +    *      The remainders suffer from float rounding, so if they are
> > +    *      within 0.000001 of an integer, we round them to integers.
> > +    */
> > +   if (month_remainder_days != (int32)month_remainder_days &&
> > +           TSROUND(month_remainder_days) == rint(month_remainder_days))
> > +           month_remainder_days = rint(month_remainder_days);
> >     result->day += (int32) month_remainder_days;
> >
> 
> Don't we want to be checking for rounding at the usec level rather  
> than 0.000001 of a day? I think this should be
> 
>       if (month_remainder_days != (int32)month_remainder_days &&
>               TSROUND(month_remainder_days * SECS_PER_DAY) ==
>               rint(month_remainder_days * SECS_PER_DAY))
>               month_remainder_days = rint(month_remainder_days);
> 

Good idea.  I updated the patch to do that.

> Another question I have concerns the month_remainder_days != (int32)  
> month_remainder_days comparison. If I understand it correctly, if the  
> TSROUND == rint portion is true, the first part is true. Or is this  
> just a quick, fast check to see if it's necessary to do a more  
> computationally intensive check?

Yea, just an optimization, but I was worried that the computations might
throw problems for certain numbers, so I figured I would only trigger it
when necessary.

> TSROUND isn't defined for HAVE_INT64_TIMESTAMP. My first attempt at  
> performing a corresponding comparison doesn't work:
> 
> +     if (month_remainder_days != (int32) month_remainder_days &&
> + #ifdef HAVE_INT64_TIMESTAMP
> +             rint(month_remainder_days * USECS_PER_DAY) ==
> +                (month_remainder_days * USECS_PER_DAY))
> + #else
> +             TSROUND(month_remainder_days * SECS_PER_DAY) ==
> +                rint(month_remainder_days * SECS_PER_DAY))
> + #endif
> +             month_remainder_days = rint(month_remainder_days);
> 
> Anyway, I'll pound on this some more tonight.

You don't want to do any conditional tests.  SECS_PER_DAY is all you
want.  Those two macros are useful only in representing the internal
storage format, not for float compuations of rounding.

Patch attached.  It also fixes a regression test output too.

-- 
  Bruce Momjian   [EMAIL PROTECTED]
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +
Index: src/backend/utils/adt/timestamp.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/adt/timestamp.c,v
retrieving revision 1.165
diff -c -c -r1.165 timestamp.c
*** src/backend/utils/adt/timestamp.c	13 Jul 2006 16:49:16 -0000	1.165
--- src/backend/utils/adt/timestamp.c	30 Aug 2006 03:45:41 -0000
***************
*** 2518,2523 ****
--- 2518,2532 ----
  
  	/* fractional months full days into days */
  	month_remainder_days = month_remainder * DAYS_PER_MONTH;
+ 	/*
+ 	 *	The remainders suffer from float rounding, so if they are
+ 	 *	within 1us of an integer, we round them to integers.
+ 	 *	It seems doing two multiplies is causing the imprecision.
+ 	 */
+ 	if (month_remainder_days != (int32)month_remainder_days &&
+ 		TSROUND(month_remainder_days * SECS_PER_DAY) ==
+ 		rint(month_remainder_days * SECS_PER_DAY))
+ 		month_remainder_days = rint(month_remainder_days);
  	result->day += (int32) month_remainder_days;
  	/* fractional months partial days into time */
  	day_remainder += month_remainder_days - (int32) month_remainder_days;
***************
*** 2571,2576 ****
--- 2580,2595 ----
  
  	/* fractional months full days into days */
  	month_remainder_days = month_remainder * DAYS_PER_MONTH;
+ 	/*
+ 	 *	The remainders suffer from float rounding, so if they are
+ 	 *	within 1us of an integer, we round them to integers.
+ 	 *	It seems doing a division and multiplies is causing the
+ 	 *	imprecision.
+ 	 */
+ 	if (month_remainder_days != (int32)month_remainder_days &&
+ 		TSROUND(month_remainder_days * SECS_PER_DAY) ==
+ 		rint(month_remainder_days * SECS_PER_DAY))
+ 		month_remainder_days = rint(month_remainder_days);
  	result->day += (int32) month_remainder_days;
  	/* fractional months partial days into time */
  	day_remainder += month_remainder_days - (int32) month_remainder_days;
Index: src/include/utils/timestamp.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/utils/timestamp.h,v
retrieving revision 1.62
diff -c -c -r1.62 timestamp.h
*** src/include/utils/timestamp.h	13 Jul 2006 16:49:20 -0000	1.62
--- src/include/utils/timestamp.h	30 Aug 2006 03:45:42 -0000
***************
*** 161,171 ****
  
  typedef double fsec_t;
  
! /* round off to MAX_TIMESTAMP_PRECISION decimal places */
! /* note: this is also used for rounding off intervals */
  #define TS_PREC_INV 1000000.0
  #define TSROUND(j) (rint(((double) (j)) * TS_PREC_INV) / TS_PREC_INV)
- #endif
  
  #define TIMESTAMP_MASK(b) (1 << (b))
  #define INTERVAL_MASK(b) (1 << (b))
--- 161,175 ----
  
  typedef double fsec_t;
  
! #endif
! 
! /*
!  *	Round off to MAX_TIMESTAMP_PRECISION decimal places.
!  *	This is also used for rounding off intervals, and
!  *	for rounding interval multiplication/division calculations.
!  */
  #define TS_PREC_INV 1000000.0
  #define TSROUND(j) (rint(((double) (j)) * TS_PREC_INV) / TS_PREC_INV)
  
  #define TIMESTAMP_MASK(b) (1 << (b))
  #define INTERVAL_MASK(b) (1 << (b))
Index: src/test/regress/expected/interval.out
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/expected/interval.out,v
retrieving revision 1.15
diff -c -c -r1.15 interval.out
*** src/test/regress/expected/interval.out	6 Mar 2006 22:49:17 -0000	1.15
--- src/test/regress/expected/interval.out	30 Aug 2006 03:45:43 -0000
***************
*** 218,224 ****
  select avg(f1) from interval_tbl;
                         avg                       
  -------------------------------------------------
!  @ 4 years 1 mon 9 days 28 hours 18 mins 23 secs
  (1 row)
  
  -- test long interval input
--- 218,224 ----
  select avg(f1) from interval_tbl;
                         avg                       
  -------------------------------------------------
!  @ 4 years 1 mon 10 days 4 hours 18 mins 23 secs
  (1 row)
  
  -- test long interval input
---------------------------(end of broadcast)---------------------------
TIP 2: Don't 'kill -9' the postmaster

Reply via email to