From d31cad7ca52580cfc7048766599e6cddeab3c61c Mon Sep 17 00:00:00 2001 From: Brian Kessler Date: Fri, 8 Jun 2018 11:48:18 -0600 Subject: [PATCH] math/big: round x + (-x) to -0 for mode ToNegativeInf MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Handling of sign bit as defined by IEEE 754-2008, section 6.3: When the sum of two operands with opposite signs (or the difference of two operands with like signs) is exactly zero, the sign of that sum (or difference) shall be +0 in all rounding-direction attributes except roundTowardNegative; under that attribute, the sign of an exact zero sum (or difference) shall be −0. However, x+x = x−(−x) retains the same sign as x even when x is zero. This change handles the special case of Add/Sub resulting in exactly zero when the rounding mode is ToNegativeInf setting the sign bit accordingly. Fixes #25798 Change-Id: I4d0715fa3c3e4a3d8a4d7861dc1d6423c8b1c68c Reviewed-on: https://go-review.googlesource.com/117495 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot Reviewed-by: Robert Griesemer --- src/math/big/float.go | 8 ++++++-- src/math/big/float_test.go | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/math/big/float.go b/src/math/big/float.go index 6f0025ec57..55b93c8915 100644 --- a/src/math/big/float.go +++ b/src/math/big/float.go @@ -1429,8 +1429,6 @@ func (x *Float) ucmp(y *Float) int { // z's accuracy reports the result error relative to the exact (not rounded) // result. Add panics with ErrNaN if x and y are infinities with opposite // signs. The value of z is undefined in that case. -// -// BUG(gri) When rounding ToNegativeInf, the sign of Float values rounded to 0 is incorrect. func (z *Float) Add(x, y *Float) *Float { if debugFloat { x.validate() @@ -1466,6 +1464,9 @@ func (z *Float) Add(x, y *Float) *Float { z.usub(y, x) } } + if z.form == zero && z.mode == ToNegativeInf && z.acc == Exact { + z.neg = true + } return z } @@ -1530,6 +1531,9 @@ func (z *Float) Sub(x, y *Float) *Float { z.usub(y, x) } } + if z.form == zero && z.mode == ToNegativeInf && z.acc == Exact { + z.neg = true + } return z } diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go index 2029409656..7d6bf034df 100644 --- a/src/math/big/float_test.go +++ b/src/math/big/float_test.go @@ -1258,6 +1258,31 @@ func TestFloatAdd(t *testing.T) { } } +// TestFloatAddRoundZero tests Float.Add/Sub rounding when the result is exactly zero. +// x + (-x) or x - x for non-zero x should be +0 in all cases except when +// the rounding mode is ToNegativeInf in which case it should be -0. +func TestFloatAddRoundZero(t *testing.T) { + for _, mode := range [...]RoundingMode{ToNearestEven, ToNearestAway, ToZero, AwayFromZero, ToPositiveInf, ToNegativeInf} { + x := NewFloat(5.0) + y := new(Float).Neg(x) + want := NewFloat(0.0) + if mode == ToNegativeInf { + want.Neg(want) + } + got := new(Float).SetMode(mode) + got.Add(x, y) + if got.Cmp(want) != 0 || got.neg != (mode == ToNegativeInf) { + t.Errorf("%s:\n\t %v\n\t+ %v\n\t= %v\n\twant %v", + mode, x, y, got, want) + } + got.Sub(x, x) + if got.Cmp(want) != 0 || got.neg != (mode == ToNegativeInf) { + t.Errorf("%v:\n\t %v\n\t- %v\n\t= %v\n\twant %v", + mode, x, x, got, want) + } + } +} + // TestFloatAdd32 tests that Float.Add/Sub of numbers with // 24bit mantissa behaves like float32 addition/subtraction // (excluding denormal numbers). -- 2.50.0