From 1948b7f8069d881822bf700185dd19dcb4b14482 Mon Sep 17 00:00:00 2001 From: Alberto Donizetti Date: Thu, 25 May 2017 11:50:40 +0200 Subject: [PATCH] math/big: fix Add, Sub when receiver aliases 2nd operand Fixes #20490 Change-Id: I9cfa604f9ff94df779cb9b4cbbd706258fc473ac Reviewed-on: https://go-review.googlesource.com/44150 Run-TryBot: Alberto Donizetti TryBot-Result: Gobot Gobot Reviewed-by: Keith Randall --- src/math/big/float.go | 13 +++++++++++-- src/math/big/float_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/math/big/float.go b/src/math/big/float.go index ac5464b127..7e11f1aff5 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 7d4bd312c9..5fd49bb894 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 -- 2.50.0