]> Cypherpunks.ru repositories - gostls13.git/commitdiff
math: add Compare and Compare32
authorAkhil Indurti <aindurti@gmail.com>
Sat, 24 Dec 2022 14:22:30 +0000 (09:22 -0500)
committerGopher Robot <gobot@golang.org>
Thu, 9 Feb 2023 20:47:48 +0000 (20:47 +0000)
This change introduces the Compare and Compare32 functions
based on the total-ordering predicate in IEEE-754, section 5.10.

In particular,
* -NaN is ordered before any other value
* +NaN is ordered after any other value
* -0 is ordered before +0
* All other values are ordered the usual way

name         time/op
Compare-8    0.24ns ± 1%
Compare32-8  0.24ns ± 0%

Fixes #56491.

Change-Id: I9444fbfefe26741794c4436a26d403b8da97bdaf
Reviewed-on: https://go-review.googlesource.com/c/go/+/459435
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
api/next/56491.txt [new file with mode: 0644]
src/math/all_test.go
src/math/compare.go [new file with mode: 0644]

diff --git a/api/next/56491.txt b/api/next/56491.txt
new file mode 100644 (file)
index 0000000..333f92e
--- /dev/null
@@ -0,0 +1,2 @@
+pkg math, func Compare(float64, float64) int #56491
+pkg math, func Compare32(float32, float32) int #56491
index 886267bc1756c108fc87f1551eeda06b60574b08..a37bf8a2bed94aac2b0f35ec5c72f68b19b55366 100644 (file)
@@ -2094,6 +2094,33 @@ var sqrt32 = []float32{
        -5.0106036182710749e+00,
 }
 
+type compareTest[F float32 | float64] struct {
+       x, y F
+       want int
+}
+
+func compareCases[F float32 | float64]() []compareTest[F] {
+       zero, nan, inf := F(0.0), F(NaN()), F(Inf(0))
+       return []compareTest[F]{
+               {-nan, -inf, -1},
+               {-inf, -nan, 1},
+               {-inf, -Pi, -1},
+               {-Pi, -inf, 1},
+               {-Pi, -zero, -1},
+               {-zero, -Pi, 1},
+               {-zero, 0, -1},
+               {0, -zero, 1},
+               {0, Pi, -1},
+               {Pi, 0, 1},
+               {Pi, inf, -1},
+               {inf, Pi, 1},
+               {inf, nan, -1},
+               {nan, inf, 1},
+               {Pi, Pi, 0},
+               {-nan, -nan, 0},
+       }
+}
+
 func tolerance(a, b, e float64) bool {
        // Multiplying by e here can underflow denormal values to zero.
        // Check a==b so that at least if a and b are small and identical
@@ -2261,6 +2288,22 @@ func TestCeil(t *testing.T) {
        }
 }
 
+func TestCompare(t *testing.T) {
+       // -NaN < -∞ < -3.14 < -0 < 0 < 3.14 < ∞ < NaN
+       for _, c := range compareCases[float64]() {
+               cmp := Compare(c.x, c.y)
+               if cmp != c.want {
+                       t.Errorf("Compare(%v, %v) = %d, want %v", c.x, c.y, cmp, c.want)
+               }
+       }
+       for _, c := range compareCases[float32]() {
+               cmp := Compare32(c.x, c.y)
+               if cmp != c.want {
+                       t.Errorf("Compare32(%v, %v) = %d, want %v", c.x, c.y, cmp, c.want)
+               }
+       }
+}
+
 func TestCopysign(t *testing.T) {
        for i := 0; i < len(vf); i++ {
                if f := Copysign(vf[i], -1); copysign[i] != f {
@@ -3320,6 +3363,22 @@ func BenchmarkCeil(b *testing.B) {
        GlobalF = x
 }
 
+func BenchmarkCompare(b *testing.B) {
+       x := 0
+       for i := 0; i < b.N; i++ {
+               x = Compare(1.5, 2.5)
+       }
+       GlobalI = x
+}
+
+func BenchmarkCompare32(b *testing.B) {
+       x := 0
+       for i := 0; i < b.N; i++ {
+               x = Compare32(1.5, 2.5)
+       }
+       GlobalI = x
+}
+
 var copysignNeg = -1.0
 
 func BenchmarkCopysign(b *testing.B) {
diff --git a/src/math/compare.go b/src/math/compare.go
new file mode 100644 (file)
index 0000000..3798110
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package math
+
+func sign[I int32 | int64](a, b I) int {
+       if a < b {
+               return -1
+       }
+       if a > b {
+               return 1
+       }
+       return 0
+}
+
+// Compare compares a and b such that
+// -NaN is ordered before any other value,
+// +NaN is ordered after any other value,
+// and -0 is ordered before +0.
+// In other words, it defines a total order over floats
+// (according to the total-ordering predicate in IEEE-754, section 5.10).
+// It returns 0 if a == b, -1 if a < b, and +1 if a > b.
+func Compare(a, b float64) int {
+       // Perform a bitwise comparison (a < b) by casting the float64s into an int64s.
+       x := int64(Float64bits(a))
+       y := int64(Float64bits(b))
+
+       // If a and b are both negative, flip the comparison so that we check a > b.
+       if x < 0 && y < 0 {
+               return sign(y, x)
+       }
+       return sign(x, y)
+}
+
+// Compare32 compares a and b such that
+// -NaN is ordered before any other value,
+// +NaN is ordered after any other value,
+// and -0 is ordered before +0.
+// In other words, it defines a total order over floats
+// (according to the total-ordering predicate in IEEE-754, section 5.10).
+// It returns 0 if a == b, -1 if a < b, and +1 if a > b.
+func Compare32(a, b float32) int {
+       // Perform a bitwise comparison (a < b) by casting the float32s into an int32s.
+       x := int32(Float32bits(a))
+       y := int32(Float32bits(b))
+
+       // If a and b are both negative, flip the comparison so that we check a > b.
+       if x < 0 && y < 0 {
+               return sign(y, x)
+       }
+       return sign(x, y)
+}