|
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 | #ifdef HAVE_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 | #ifdef HAVE_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);
|
|
0 commit comments