]> Cypherpunks repositories - gostls13.git/commitdiff
math: Add Round function (ties away from zero)
authorMark Pulford <mark@kyne.com.au>
Thu, 18 May 2017 12:15:18 +0000 (22:15 +1000)
committerRobert Griesemer <gri@golang.org>
Sat, 2 Sep 2017 21:00:08 +0000 (21:00 +0000)
This function avoids subtle faults found in many ad-hoc implementations,
and is simple enough to be inlined by the compiler.

Fixes #20100

Change-Id: Ib320254e9b1f1f798c6ef906b116f63bc29e8d08
Reviewed-on: https://go-review.googlesource.com/43652
Reviewed-by: Robert Griesemer <gri@golang.org>
src/math/all_test.go
src/math/floor.go

index 89abcf063df0c5a8b5d44ab8a0feb85e85f34cf8..7409d8b141a71f2483528a2c7232d78e352c1445 100644 (file)
@@ -529,6 +529,18 @@ var remainder = []float64{
        8.734595415957246977711748e-01,
        1.314075231424398637614104e+00,
 }
+var round = []float64{
+       5,
+       8,
+       Copysign(0, -1),
+       -5,
+       10,
+       3,
+       5,
+       3,
+       2,
+       -9,
+}
 var signbit = []bool{
        false,
        false,
@@ -1755,6 +1767,20 @@ var pow10SC = []float64{
        Inf(1),   // pow10(MaxInt32)
 }
 
+var vfroundSC = [][2]float64{
+       {0, 0},
+       {1.390671161567e-309, 0}, // denormal
+       {0.49999999999999994, 0}, // 0.5-epsilon
+       {0.5, 1},
+       {0.5000000000000001, 1}, // 0.5+epsilon
+       {-1.5, -2},
+       {NaN(), NaN()},
+       {Inf(1), Inf(1)},
+       {2251799813685249.5, 2251799813685250}, // 1 bit fraction
+       {4503599627370495.5, 4503599627370496}, // 1 bit fraction, rounding to 0 bit fraction
+       {4503599627370497, 4503599627370497},   // large integer
+}
+
 var vfsignbitSC = []float64{
        Inf(-1),
        Copysign(0, -1),
@@ -2713,6 +2739,19 @@ func TestRemainder(t *testing.T) {
        }
 }
 
+func TestRound(t *testing.T) {
+       for i := 0; i < len(vf); i++ {
+               if f := Round(vf[i]); !alike(round[i], f) {
+                       t.Errorf("Round(%g) = %g, want %g", vf[i], f, round[i])
+               }
+       }
+       for i := 0; i < len(vfroundSC); i++ {
+               if f := Round(vfroundSC[i][0]); !alike(vfroundSC[i][1], f) {
+                       t.Errorf("Round(%g) = %g, want %g", vfroundSC[i][0], f, vfroundSC[i][1])
+               }
+       }
+}
+
 func TestSignbit(t *testing.T) {
        for i := 0; i < len(vf); i++ {
                if f := Signbit(vf[i]); signbit[i] != f {
@@ -3360,6 +3399,16 @@ func BenchmarkPow10Neg(b *testing.B) {
        GlobalF = x
 }
 
+var roundNeg = float64(-2.5)
+
+func BenchmarkRound(b *testing.B) {
+       x := 0.0
+       for i := 0; i < b.N; i++ {
+               x = Round(roundNeg)
+       }
+       GlobalF = x
+}
+
 func BenchmarkRemainder(b *testing.B) {
        x := 0.0
        for i := 0; i < b.N; i++ {
index 9d30629c5e9c08bad32652720cd246116dd88c3b..d03c4bcdadbc4b3dc3760ac70b1afba4ad018326 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2009-2010 The Go Authors. All rights reserved.
+// Copyright 2009 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
@@ -54,3 +54,46 @@ func trunc(x float64) float64 {
        d, _ := Modf(x)
        return d
 }
+
+// Round returns the nearest integer, rounding half away from zero.
+//
+// Special cases are:
+//     Round(±0) = ±0
+//     Round(±Inf) = ±Inf
+//     Round(NaN) = NaN
+func Round(x float64) float64 {
+       // Round is a faster implementation of:
+       //
+       // func Round(x float64) float64 {
+       //   t := Trunc(x)
+       //   if Abs(x-t) >= 0.5 {
+       //     return t + Copysign(1, x)
+       //   }
+       //   return t
+       // }
+       const (
+               signMask = 1 << 63
+               fracMask = 1<<shift - 1
+               half     = 1 << (shift - 1)
+               one      = bias << shift
+       )
+
+       bits := Float64bits(x)
+       e := uint(bits>>shift) & mask
+       if e < bias {
+               // Round abs(x) < 1 including denormals.
+               bits &= signMask // +-0
+               if e == bias-1 {
+                       bits |= one // +-1
+               }
+       } else if e < bias+shift {
+               // Round any abs(x) >= 1 containing a fractional component [0,1).
+               //
+               // Numbers with larger exponents are returned unchanged since they
+               // must be either an integer, infinity, or NaN.
+               e -= bias
+               bits += half >> e
+               bits &^= fracMask >> e
+       }
+       return Float64frombits(bits)
+}