Commit 03c3bb5f authored by Mark Pulford's avatar Mark Pulford Committed by Robert Griesemer

math: Add Round function (ties away from zero)

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/43652Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent dbe3522c
...@@ -529,6 +529,18 @@ var remainder = []float64{ ...@@ -529,6 +529,18 @@ var remainder = []float64{
8.734595415957246977711748e-01, 8.734595415957246977711748e-01,
1.314075231424398637614104e+00, 1.314075231424398637614104e+00,
} }
var round = []float64{
5,
8,
Copysign(0, -1),
-5,
10,
3,
5,
3,
2,
-9,
}
var signbit = []bool{ var signbit = []bool{
false, false,
false, false,
...@@ -1755,6 +1767,20 @@ var pow10SC = []float64{ ...@@ -1755,6 +1767,20 @@ var pow10SC = []float64{
Inf(1), // pow10(MaxInt32) 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{ var vfsignbitSC = []float64{
Inf(-1), Inf(-1),
Copysign(0, -1), Copysign(0, -1),
...@@ -2713,6 +2739,19 @@ func TestRemainder(t *testing.T) { ...@@ -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) { func TestSignbit(t *testing.T) {
for i := 0; i < len(vf); i++ { for i := 0; i < len(vf); i++ {
if f := Signbit(vf[i]); signbit[i] != f { if f := Signbit(vf[i]); signbit[i] != f {
...@@ -3360,6 +3399,16 @@ func BenchmarkPow10Neg(b *testing.B) { ...@@ -3360,6 +3399,16 @@ func BenchmarkPow10Neg(b *testing.B) {
GlobalF = x 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) { func BenchmarkRemainder(b *testing.B) {
x := 0.0 x := 0.0
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
......
// 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 // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
...@@ -54,3 +54,46 @@ func trunc(x float64) float64 { ...@@ -54,3 +54,46 @@ func trunc(x float64) float64 {
d, _ := Modf(x) d, _ := Modf(x)
return d 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)
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment