]> Cypherpunks repositories - gostls13.git/commitdiff
math/big: round x + (-x) to -0 for mode ToNegativeInf
authorBrian Kessler <brian.m.kessler@gmail.com>
Fri, 8 Jun 2018 17:48:18 +0000 (11:48 -0600)
committerRobert Griesemer <gri@golang.org>
Thu, 14 Jun 2018 21:07:01 +0000 (21:07 +0000)
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: Robert Griesemer <gri@golang.org>
src/math/big/float.go
src/math/big/float_test.go

index 6f0025ec57147ccbdf61658d8c63db790da09834..55b93c8915072a9e70c387b026f52fa71067e15c 100644 (file)
@@ -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
        }
 
index 20294096563e7e367350ea7739314f3b259425bc..7d6bf034dff8103605f919eb003a2144fefd5167 100644 (file)
@@ -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).