Min/Max for integers?
Writing a function that returns the max value of a type [~float64 | ~uint | ~int T]
Is there no Min/Max function for integers in Golang in the standard library?
This would be a good use-case for generics.
Starting with Go 1.21, min and max are available as builtins and you do not need to write them at all. (Thanks @ufukty for highlighting this in a comment.)
I'm leaving the previous content below in case someone needs a different simple generic function that isn't built in or can otherwise use the historical answers.
Until Go 1.18 a one-off function was the standard way; for example, the stdlib's sort.go does it near the top of the file:
func min(a, b int) int {
if a < b {
return a
}
return b
}
You might still want or need to use this approach so your code works on Go versions below 1.18!
Starting with Go 1.18, you can write a generic min function which is just as efficient at run time as the hand-coded single-type version, but works with any type with < and > operators:
func minT constraints.Ordered T {
if a < b {
return a
}
return b
}
func main() {
fmt.Println(min(1, 2))
fmt.Println(min(1.5, 2.5))
fmt.Println(min("Hello", "世界"))
}
There's been discussion of updating the stdlib to add generic versions of existing functions, but if that happens it won't be until a later version.
math.Min(2, 3) happened to work because numeric constants in Go are untyped. Beware of treating float64s as a universal number type in general, though, since integers above 2^53 will get rounded if converted to float64.
There is no built-in min or max function for integers, but it’s simple to write your own. Thanks to support for variadic functions we can even compare more integers with just one call:
func MinOf(vars ...int) int {
min := vars[0]
for _, i := range vars {
if min > i {
min = i
}
}
return min
}
Usage:
MinOf(3, 9, 6, 2)
Similarly here is the max function:
func MaxOf(vars ...int) int {
max := vars[0]
for _, i := range vars {
if max < i {
max = i
}
}
return max
}
Today I was trying to write a generic function to return the maximum value for floats, ints and uints. It seems straightforward, right? It's not!
The function signature:
type Types interface {
~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uint |
~int | ~int64 | ~int32 | ~int16 | ~int8 |
~float64 | ~float32
}
func infFor[T Types]() TMy first thought was to write a switch checking the type, like this:
func infFor[T Types]() T {
var v T
switch any(v).T {
case float64:
return T(math.Inf(1))
case int8:
return T(math.MaxInt8)
...
}
}
But this doesn't work for user defined types (type myInt int).
Then I tried to split the function in two parts, check if T is a float or (u)int, this makes the job easier.
func infFor[T Types]() T {
// Check if T is a float.
var f float64 = 1.5
if float64(T(f)) == f {
return T(math.Inf(1))
}
// Handle (u)ints ...
}Converting 1.5 to an integer type will truncate the value and then float64(T(f)) == f is false.
The value 1.5 is important because it can be represented both by float64 and float32, 1.1 can't.
After the check we know the value is an integer, but the compiler doesn't, so we can't use bit arithmetic. The solution I found is to check when the value overflows.
This is the final version:
func infFor[T Types]() T {
// Check if T is a float.
var f float64 = 1.5
if float64(T(f)) == f {
return T(math.Inf(1))
}
maxValues := [...]uint64{
math.MaxInt8,
math.MaxUint8,
math.MaxInt16,
math.MaxUint16,
math.MaxInt32,
math.MaxUint32,
math.MaxInt64,
math.MaxUint64,
}
var v T
// Check when v overflows.
for i := 0; v+1 > 0; i++ {
v = T(maxValues[i])
}
return v
}