diff --git a/.golangci.yml b/.golangci.yml index ed338e60..c6b0f4e6 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -122,3 +122,9 @@ issues: text: "comment on exported const .+ should be of the form" - linters: [revive] text: "exported const .+ should have comment" + + # False positive. + - path: internal/cmp + linters: [gocritic] + source: "return x != x" + text: "dupSubExpr" diff --git a/internal/cmp/cmp.go b/internal/cmp/cmp.go index d089aa04..5feaff37 100644 --- a/internal/cmp/cmp.go +++ b/internal/cmp/cmp.go @@ -3,14 +3,34 @@ package cmp import "golang.org/x/exp/constraints" -// Compare is a comparator. -func Compare[T constraints.Ordered](a, b T) int { - switch { - case a < b: - return -1 - case a > b: - return 1 - default: +// Ordered is a type alias to [constraints.Ordered]. +type Ordered = constraints.Ordered + +// Compare returns +// +// -1 if x is less than y, +// 0 if x equals y, +// +1 if x is greater than y. +// +// For floating-point types, a NaN is considered less than any non-NaN, +// a NaN is considered equal to a NaN, and -0.0 is equal to 0.0. +func Compare[T Ordered](x, y T) int { + xNaN := isNaN(x) + yNaN := isNaN(y) + if xNaN && yNaN { return 0 } + if xNaN || x < y { + return -1 + } + if yNaN || x > y { + return +1 + } + return 0 +} + +// isNaN reports whether x is a NaN without requiring the math package. +// This will always return false if T is not floating-point. +func isNaN[T Ordered](x T) bool { + return x != x }