diff --git a/src/math/big/float.go b/src/math/big/float.go index ac5464b1279eb..7e11f1aff5815 100644 --- a/src/math/big/float.go +++ b/src/math/big/float.go @@ -1439,8 +1439,16 @@ func (z *Float) Add(x, y *Float) *Float { if x.form == finite && y.form == finite { // x + y (common case) + + // Below we set z.neg = x.neg, and when z aliases y this will + // change the y operand's sign. This is fine, because if an + // operand aliases the receiver it'll be overwritten, but we still + // want the original x.neg and y.neg values when we evaluate + // x.neg != y.neg, so we need to save y.neg before setting z.neg. + yneg := y.neg + z.neg = x.neg - if x.neg == y.neg { + if x.neg == yneg { // x + y == x + y // (-x) + (-y) == -(x + y) z.uadd(x, y) @@ -1502,8 +1510,9 @@ func (z *Float) Sub(x, y *Float) *Float { if x.form == finite && y.form == finite { // x - y (common case) + yneg := y.neg z.neg = x.neg - if x.neg != y.neg { + if x.neg != yneg { // x - (-y) == x + y // (-x) - y == -(x + y) z.uadd(x, y) diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go index 7d4bd312c9bd4..5fd49bb89462e 100644 --- a/src/math/big/float_test.go +++ b/src/math/big/float_test.go @@ -1325,6 +1325,34 @@ func TestFloatAdd64(t *testing.T) { } } +func TestIssue20490(t *testing.T) { + var tests = []struct { + a, b float64 + }{ + {4, 1}, + {-4, 1}, + {4, -1}, + {-4, -1}, + } + + for _, test := range tests { + a, b := NewFloat(test.a), NewFloat(test.b) + diff := new(Float).Sub(a, b) + b.Sub(a, b) + if b.Cmp(diff) != 0 { + t.Errorf("got %g - %g = %g; want %g\n", a, NewFloat(test.b), b, diff) + } + + b = NewFloat(test.b) + sum := new(Float).Add(a, b) + b.Add(a, b) + if b.Cmp(sum) != 0 { + t.Errorf("got %g + %g = %g; want %g\n", a, NewFloat(test.b), b, sum) + } + + } +} + // TestFloatMul tests Float.Mul/Quo by comparing the result of a "manual" // multiplication/division of arguments represented by Bits values with the // respective Float multiplication/division for a variety of precisions