Commit d31cad7c authored by Brian Kessler's avatar Brian Kessler Committed by Robert Griesemer

math/big: round x + (-x) to -0 for mode ToNegativeInf

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 <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: 's avatarRobert Griesemer <gri@golang.org>
parent c359d759
......@@ -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
}
......
......@@ -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).
......
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