Skip to content

Commit 03c3bb5

Browse files
mpxgriesemer
authored andcommitted
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/43652 Reviewed-by: Robert Griesemer <gri@golang.org>
1 parent dbe3522 commit 03c3bb5

File tree

2 files changed

+93
-1
lines changed

2 files changed

+93
-1
lines changed

src/math/all_test.go

+49
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,18 @@ var remainder = []float64{
529529
8.734595415957246977711748e-01,
530530
1.314075231424398637614104e+00,
531531
}
532+
var round = []float64{
533+
5,
534+
8,
535+
Copysign(0, -1),
536+
-5,
537+
10,
538+
3,
539+
5,
540+
3,
541+
2,
542+
-9,
543+
}
532544
var signbit = []bool{
533545
false,
534546
false,
@@ -1755,6 +1767,20 @@ var pow10SC = []float64{
17551767
Inf(1), // pow10(MaxInt32)
17561768
}
17571769

1770+
var vfroundSC = [][2]float64{
1771+
{0, 0},
1772+
{1.390671161567e-309, 0}, // denormal
1773+
{0.49999999999999994, 0}, // 0.5-epsilon
1774+
{0.5, 1},
1775+
{0.5000000000000001, 1}, // 0.5+epsilon
1776+
{-1.5, -2},
1777+
{NaN(), NaN()},
1778+
{Inf(1), Inf(1)},
1779+
{2251799813685249.5, 2251799813685250}, // 1 bit fraction
1780+
{4503599627370495.5, 4503599627370496}, // 1 bit fraction, rounding to 0 bit fraction
1781+
{4503599627370497, 4503599627370497}, // large integer
1782+
}
1783+
17581784
var vfsignbitSC = []float64{
17591785
Inf(-1),
17601786
Copysign(0, -1),
@@ -2713,6 +2739,19 @@ func TestRemainder(t *testing.T) {
27132739
}
27142740
}
27152741

2742+
func TestRound(t *testing.T) {
2743+
for i := 0; i < len(vf); i++ {
2744+
if f := Round(vf[i]); !alike(round[i], f) {
2745+
t.Errorf("Round(%g) = %g, want %g", vf[i], f, round[i])
2746+
}
2747+
}
2748+
for i := 0; i < len(vfroundSC); i++ {
2749+
if f := Round(vfroundSC[i][0]); !alike(vfroundSC[i][1], f) {
2750+
t.Errorf("Round(%g) = %g, want %g", vfroundSC[i][0], f, vfroundSC[i][1])
2751+
}
2752+
}
2753+
}
2754+
27162755
func TestSignbit(t *testing.T) {
27172756
for i := 0; i < len(vf); i++ {
27182757
if f := Signbit(vf[i]); signbit[i] != f {
@@ -3360,6 +3399,16 @@ func BenchmarkPow10Neg(b *testing.B) {
33603399
GlobalF = x
33613400
}
33623401

3402+
var roundNeg = float64(-2.5)
3403+
3404+
func BenchmarkRound(b *testing.B) {
3405+
x := 0.0
3406+
for i := 0; i < b.N; i++ {
3407+
x = Round(roundNeg)
3408+
}
3409+
GlobalF = x
3410+
}
3411+
33633412
func BenchmarkRemainder(b *testing.B) {
33643413
x := 0.0
33653414
for i := 0; i < b.N; i++ {

src/math/floor.go

+44-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2009-2010 The Go Authors. All rights reserved.
1+
// Copyright 2009 The Go Authors. All rights reserved.
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

@@ -54,3 +54,46 @@ func trunc(x float64) float64 {
5454
d, _ := Modf(x)
5555
return d
5656
}
57+
58+
// Round returns the nearest integer, rounding half away from zero.
59+
//
60+
// Special cases are:
61+
// Round(±0) = ±0
62+
// Round(±Inf) = ±Inf
63+
// Round(NaN) = NaN
64+
func Round(x float64) float64 {
65+
// Round is a faster implementation of:
66+
//
67+
// func Round(x float64) float64 {
68+
// t := Trunc(x)
69+
// if Abs(x-t) >= 0.5 {
70+
// return t + Copysign(1, x)
71+
// }
72+
// return t
73+
// }
74+
const (
75+
signMask = 1 << 63
76+
fracMask = 1<<shift - 1
77+
half = 1 << (shift - 1)
78+
one = bias << shift
79+
)
80+
81+
bits := Float64bits(x)
82+
e := uint(bits>>shift) & mask
83+
if e < bias {
84+
// Round abs(x) < 1 including denormals.
85+
bits &= signMask // +-0
86+
if e == bias-1 {
87+
bits |= one // +-1
88+
}
89+
} else if e < bias+shift {
90+
// Round any abs(x) >= 1 containing a fractional component [0,1).
91+
//
92+
// Numbers with larger exponents are returned unchanged since they
93+
// must be either an integer, infinity, or NaN.
94+
e -= bias
95+
bits += half >> e
96+
bits &^= fracMask >> e
97+
}
98+
return Float64frombits(bits)
99+
}

0 commit comments

Comments
 (0)