|
8 | 8 | *
|
9 | 9 | *
|
10 | 10 | * IDENTIFICATION
|
11 |
| - * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.166 2006/09/03 03:34:04 momjian Exp $ |
| 11 | + * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.167 2006/09/05 01:13:39 momjian Exp $ |
12 | 12 | *
|
13 | 13 | *-------------------------------------------------------------------------
|
14 | 14 | */
|
@@ -2514,28 +2514,34 @@ interval_mul(PG_FUNCTION_ARGS)
|
2514 | 2514 | /*
|
2515 | 2515 | *Fractional months full days into days.
|
2516 | 2516 | *
|
2517 |
| - *The remainders suffer from float rounding, so instead of |
2518 |
| - *doing the computation using just the remainder, we calculate |
2519 |
| - *the total number of days and subtract. Specifically, we are |
2520 |
| - *multipling by DAYS_PER_MONTH before dividing by factor. |
2521 |
| - *This greatly reduces rounding errors. |
| 2517 | + *Floating point calculation are inherently inprecise, so these |
| 2518 | + *calculations are crafted to produce the most reliable result |
| 2519 | + *possible. TSROUND() is needed to more accurately produce whole |
| 2520 | + *numbers where appropriate. |
2522 | 2521 | */
|
2523 |
| -month_remainder_days= (orig_month* (double)DAYS_PER_MONTH)*factor- |
2524 |
| -result->month* (double)DAYS_PER_MONTH; |
2525 |
| -sec_remainder= (orig_day* (double)SECS_PER_DAY)*factor- |
2526 |
| -result->day* (double)SECS_PER_DAY+ |
2527 |
| -(month_remainder_days- (int32)month_remainder_days)*SECS_PER_DAY; |
| 2522 | +month_remainder_days= (orig_month*factor-result->month)*DAYS_PER_MONTH; |
| 2523 | +month_remainder_days=TSROUND(month_remainder_days); |
| 2524 | +sec_remainder= (orig_day*factor-result->day+ |
| 2525 | +month_remainder_days- (int)month_remainder_days)*SECS_PER_DAY; |
| 2526 | +sec_remainder=TSROUND(sec_remainder); |
| 2527 | + |
| 2528 | +/* |
| 2529 | + *Might have 24:00:00 hours due to rounding, or >24 hours because of |
| 2530 | + *time cascade from months and days. It might still be >24 if the |
| 2531 | + *combination of cascade and the seconds factor operation itself. |
| 2532 | + */ |
| 2533 | +if (Abs(sec_remainder) >=SECS_PER_DAY) |
| 2534 | +{ |
| 2535 | +result->day+= (int)(sec_remainder /SECS_PER_DAY); |
| 2536 | +sec_remainder-= (int)(sec_remainder /SECS_PER_DAY)*SECS_PER_DAY; |
| 2537 | +} |
2528 | 2538 |
|
2529 | 2539 | /* cascade units down */
|
2530 | 2540 | result->day+= (int32)month_remainder_days;
|
2531 | 2541 | #ifdefHAVE_INT64_TIMESTAMP
|
2532 | 2542 | result->time=rint(span->time*factor+sec_remainder*USECS_PER_SEC);
|
2533 | 2543 | #else
|
2534 |
| -/* |
2535 |
| - *TSROUND() needed to prevent -146:23:60.00 output on PowerPC for |
2536 |
| - *SELECT interval '-41 mon -12 days -360:00' * 0.3; |
2537 |
| - */ |
2538 |
| -result->time=span->time*factor+TSROUND(sec_remainder); |
| 2544 | +result->time=span->time*factor+sec_remainder; |
2539 | 2545 | #endif
|
2540 | 2546 |
|
2541 | 2547 | PG_RETURN_INTERVAL_P(result);
|
@@ -2574,19 +2580,24 @@ interval_div(PG_FUNCTION_ARGS)
|
2574 | 2580 | *Fractional months full days into days. See comment in
|
2575 | 2581 | *interval_mul().
|
2576 | 2582 | */
|
2577 |
| -month_remainder_days= (orig_month* (double)DAYS_PER_MONTH) /factor- |
2578 |
| -result->month* (double)DAYS_PER_MONTH; |
2579 |
| -sec_remainder= (orig_day* (double)SECS_PER_DAY) /factor- |
2580 |
| -result->day* (double)SECS_PER_DAY+ |
2581 |
| -(month_remainder_days- (int32)month_remainder_days)*SECS_PER_DAY; |
| 2583 | +month_remainder_days= (orig_month /factor-result->month)*DAYS_PER_MONTH; |
| 2584 | +month_remainder_days=TSROUND(month_remainder_days); |
| 2585 | +sec_remainder= (orig_day /factor-result->day+ |
| 2586 | +month_remainder_days- (int)month_remainder_days)*SECS_PER_DAY; |
| 2587 | +sec_remainder=TSROUND(sec_remainder); |
| 2588 | +if (Abs(sec_remainder) >=SECS_PER_DAY) |
| 2589 | +{ |
| 2590 | +result->day+= (int)(sec_remainder /SECS_PER_DAY); |
| 2591 | +sec_remainder-= (int)(sec_remainder /SECS_PER_DAY)*SECS_PER_DAY; |
| 2592 | +} |
2582 | 2593 |
|
2583 | 2594 | /* cascade units down */
|
2584 | 2595 | result->day+= (int32)month_remainder_days;
|
2585 | 2596 | #ifdefHAVE_INT64_TIMESTAMP
|
2586 | 2597 | result->time=rint(span->time /factor+sec_remainder*USECS_PER_SEC);
|
2587 | 2598 | #else
|
2588 | 2599 | /* See TSROUND comment in interval_mul(). */
|
2589 |
| -result->time=span->time /factor+TSROUND(sec_remainder); |
| 2600 | +result->time=span->time /factor+sec_remainder; |
2590 | 2601 | #endif
|
2591 | 2602 |
|
2592 | 2603 | PG_RETURN_INTERVAL_P(result);
|
|