From: Russ Cox Date: Fri, 31 Mar 2017 16:34:25 +0000 (-0400) Subject: time: test and fix Time.Round, Duration.Round for d > 2⁶² X-Git-Tag: go1.9beta1~865 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=105cc2bd6396f47bc613721fb6c1db66050e15ab;p=gostls13.git time: test and fix Time.Round, Duration.Round for d > 2⁶² Round uses r+r < d to decide whether the remainder is above or below half of d (to decide whether to round up or down). This is wrong when r+r wraps negative, because it looks < d but is really > d. No one will ever care about rounding to a multiple of d > 2⁶² (about 146 years), but might as well get it right. Fixes #19807. Change-Id: I1b55a742dc36e02a7465bc778bf5dd74fe71f7c0 Reviewed-on: https://go-review.googlesource.com/39151 Run-TryBot: Russ Cox Reviewed-by: Rob Pike Reviewed-by: David Chase TryBot-Result: Gobot Gobot --- diff --git a/src/time/time.go b/src/time/time.go index 5bb7dd961d..5283b7eb21 100644 --- a/src/time/time.go +++ b/src/time/time.go @@ -798,6 +798,12 @@ func (d Duration) Truncate(m Duration) Duration { return d - d%m } +// lessThanHalf reports whether x+x < y but avoids overflow, +// assuming x and y are both positive (Duration is signed). +func lessThanHalf(x, y Duration) bool { + return uint64(x)+uint64(x) < uint64(y) +} + // Round returns the result of rounding d to the nearest multiple of m. // The rounding behavior for halfway values is to round away from zero. // If the result exceeds the maximum (or minimum) @@ -811,7 +817,7 @@ func (d Duration) Round(m Duration) Duration { r := d % m if d < 0 { r = -r - if r+r < m { + if lessThanHalf(r, m) { return d + r } if d1 := d - m + r; d1 < d { @@ -819,7 +825,7 @@ func (d Duration) Round(m Duration) Duration { } return minDuration // overflow } - if r+r < m { + if lessThanHalf(r, m) { return d - r } if d1 := d + m - r; d1 > d { @@ -1400,7 +1406,7 @@ func (t Time) Round(d Duration) Time { return t } _, r := div(t, d) - if r+r < d { + if lessThanHalf(r, d) { return t.Add(-r) } return t.Add(d - r) diff --git a/src/time/time_test.go b/src/time/time_test.go index ebe28e61f4..dba8e0dadc 100644 --- a/src/time/time_test.go +++ b/src/time/time_test.go @@ -233,6 +233,7 @@ var truncateRoundTests = []struct { {Date(-1, January, 1, 12, 15, 31, 5e8, UTC), 3}, {Date(2012, January, 1, 12, 15, 30, 5e8, UTC), Second}, {Date(2012, January, 1, 12, 15, 31, 5e8, UTC), Second}, + {Unix(-19012425939, 649146258), 7435029458905025217}, // 5.8*d rounds to 6*d, but .8*d+.8*d < 0 < d } func TestTruncateRound(t *testing.T) { @@ -1107,6 +1108,7 @@ var durationRoundTests = []struct { {9e18, 5e18, 1<<63 - 1}, {-8e18, 3e18, -9e18}, {-9e18, 5e18, -1 << 63}, + {3<<61 - 1, 3 << 61, 3 << 61}, } func TestDurationRound(t *testing.T) {