Source file src/runtime/minmax_test.go

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package runtime_test
     6  
     7  import (
     8  	"math"
     9  	"strings"
    10  	"testing"
    11  	"unsafe"
    12  )
    13  
    14  var (
    15  	zero    = math.Copysign(0, +1)
    16  	negZero = math.Copysign(0, -1)
    17  	inf     = math.Inf(+1)
    18  	negInf  = math.Inf(-1)
    19  	nan     = math.NaN()
    20  )
    21  
    22  var tests = []struct{ min, max float64 }{
    23  	{1, 2},
    24  	{-2, 1},
    25  	{negZero, zero},
    26  	{zero, inf},
    27  	{negInf, zero},
    28  	{negInf, inf},
    29  	{1, inf},
    30  	{negInf, 1},
    31  }
    32  
    33  var all = []float64{1, 2, -1, -2, zero, negZero, inf, negInf, nan}
    34  
    35  func eq(x, y float64) bool {
    36  	return x == y && math.Signbit(x) == math.Signbit(y)
    37  }
    38  
    39  func TestMinFloat(t *testing.T) {
    40  	for _, tt := range tests {
    41  		if z := min(tt.min, tt.max); !eq(z, tt.min) {
    42  			t.Errorf("min(%v, %v) = %v, want %v", tt.min, tt.max, z, tt.min)
    43  		}
    44  		if z := min(tt.max, tt.min); !eq(z, tt.min) {
    45  			t.Errorf("min(%v, %v) = %v, want %v", tt.max, tt.min, z, tt.min)
    46  		}
    47  	}
    48  	for _, x := range all {
    49  		if z := min(nan, x); !math.IsNaN(z) {
    50  			t.Errorf("min(%v, %v) = %v, want %v", nan, x, z, nan)
    51  		}
    52  		if z := min(x, nan); !math.IsNaN(z) {
    53  			t.Errorf("min(%v, %v) = %v, want %v", nan, x, z, nan)
    54  		}
    55  	}
    56  }
    57  
    58  func TestMaxFloat(t *testing.T) {
    59  	for _, tt := range tests {
    60  		if z := max(tt.min, tt.max); !eq(z, tt.max) {
    61  			t.Errorf("max(%v, %v) = %v, want %v", tt.min, tt.max, z, tt.max)
    62  		}
    63  		if z := max(tt.max, tt.min); !eq(z, tt.max) {
    64  			t.Errorf("max(%v, %v) = %v, want %v", tt.max, tt.min, z, tt.max)
    65  		}
    66  	}
    67  	for _, x := range all {
    68  		if z := max(nan, x); !math.IsNaN(z) {
    69  			t.Errorf("max(%v, %v) = %v, want %v", nan, x, z, nan)
    70  		}
    71  		if z := max(x, nan); !math.IsNaN(z) {
    72  			t.Errorf("max(%v, %v) = %v, want %v", nan, x, z, nan)
    73  		}
    74  	}
    75  }
    76  
    77  // testMinMax tests that min/max behave correctly on every pair of
    78  // values in vals.
    79  //
    80  // vals should be a sequence of values in strictly ascending order.
    81  func testMinMax[T int | uint8 | string](t *testing.T, vals ...T) {
    82  	for i, x := range vals {
    83  		for _, y := range vals[i+1:] {
    84  			if !(x < y) {
    85  				t.Fatalf("values out of order: !(%v < %v)", x, y)
    86  			}
    87  
    88  			if z := min(x, y); z != x {
    89  				t.Errorf("min(%v, %v) = %v, want %v", x, y, z, x)
    90  			}
    91  			if z := min(y, x); z != x {
    92  				t.Errorf("min(%v, %v) = %v, want %v", y, x, z, x)
    93  			}
    94  
    95  			if z := max(x, y); z != y {
    96  				t.Errorf("max(%v, %v) = %v, want %v", x, y, z, y)
    97  			}
    98  			if z := max(y, x); z != y {
    99  				t.Errorf("max(%v, %v) = %v, want %v", y, x, z, y)
   100  			}
   101  		}
   102  	}
   103  }
   104  
   105  func TestMinMaxInt(t *testing.T)    { testMinMax[int](t, -7, 0, 9) }
   106  func TestMinMaxUint8(t *testing.T)  { testMinMax[uint8](t, 0, 1, 2, 4, 7) }
   107  func TestMinMaxString(t *testing.T) { testMinMax[string](t, "a", "b", "c") }
   108  
   109  // TestMinMaxStringTies ensures that min(a, b) returns a when a == b.
   110  func TestMinMaxStringTies(t *testing.T) {
   111  	s := "xxx"
   112  	x := strings.Split(s, "")
   113  
   114  	test := func(i, j, k int) {
   115  		if z := min(x[i], x[j], x[k]); unsafe.StringData(z) != unsafe.StringData(x[i]) {
   116  			t.Errorf("min(x[%v], x[%v], x[%v]) = %p, want %p", i, j, k, unsafe.StringData(z), unsafe.StringData(x[i]))
   117  		}
   118  		if z := max(x[i], x[j], x[k]); unsafe.StringData(z) != unsafe.StringData(x[i]) {
   119  			t.Errorf("max(x[%v], x[%v], x[%v]) = %p, want %p", i, j, k, unsafe.StringData(z), unsafe.StringData(x[i]))
   120  		}
   121  	}
   122  
   123  	test(0, 1, 2)
   124  	test(0, 2, 1)
   125  	test(1, 0, 2)
   126  	test(1, 2, 0)
   127  	test(2, 0, 1)
   128  	test(2, 1, 0)
   129  }
   130  
   131  func BenchmarkMinFloat(b *testing.B) {
   132  	var m float64 = 0
   133  	for i := 0; i < b.N; i++ {
   134  		for _, f := range all {
   135  			m = min(m, f)
   136  		}
   137  	}
   138  }
   139  
   140  func BenchmarkMaxFloat(b *testing.B) {
   141  	var m float64 = 0
   142  	for i := 0; i < b.N; i++ {
   143  		for _, f := range all {
   144  			m = max(m, f)
   145  		}
   146  	}
   147  }
   148  

View as plain text