x/exp/stats: use ~float64 per proposal spec
Updates golang/go#69264
Change-Id: I8842c5e747984c56e13b5d120a8760313bdc710a
Reviewed-on: https://go-review.googlesource.com/c/exp/+/688795
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/stats/stats.go b/stats/stats.go
index bc3e2ff..c6803c2 100644
--- a/stats/stats.go
+++ b/stats/stats.go
@@ -40,15 +40,15 @@
//
// If values contains NaN or both Inf and -Inf, it returns NaN.
// If values contains Inf, it returns Inf. If values contains -Inf, it returns -Inf.
-func Mean(values []float64) float64 {
+func Mean[F ~float64](values []F) F {
mean, infs := meanInf(values)
switch infs {
case negInf:
- return math.Inf(-1)
+ return F(math.Inf(-1))
case posInf:
- return math.Inf(1)
+ return F(math.Inf(1))
case negInf | posInf:
- return math.NaN()
+ return F(math.NaN())
default: // passthrough mean or NaN
}
return mean
@@ -66,44 +66,44 @@
// If values contains both Inf and -Inf, it returns NaN, Inf.
// If values contains Inf, it returns Inf, Inf.
// If values contains -Inf, it returns -Inf, Inf.
-func MeanAndStdDev(values []float64) (float64, float64) {
+func MeanAndStdDev[F ~float64](values []F) (F, F) {
mean, infs := meanInf(values)
switch infs {
case 0:
- if math.IsNaN(mean) {
- return mean, math.NaN()
+ if math.IsNaN(float64(mean)) {
+ return mean, F(math.NaN())
}
case negInf, posInf, negInf | posInf:
- return mean, math.Inf(1)
+ return mean, F(math.Inf(1))
}
if len(values) == 1 {
return mean, 0
}
- squaredDiffs := 0.0
+ squaredDiffs := F(0.0)
for _, v := range values {
diff := v - mean
squaredDiffs += diff * diff
}
- return mean, math.Sqrt(squaredDiffs / float64(len(values)-1))
+ return mean, F(math.Sqrt(float64(squaredDiffs) / float64(len(values)-1)))
}
// meanInf calculates a naive mean value
// and reports the infinities status.
-func meanInf(values []float64) (float64, infinities) {
+func meanInf[F ~float64](values []F) (F, infinities) {
if len(values) == 0 {
panic("mean: empty slice")
}
- sum, infs := 0.0, infinities(0)
+ sum, infs := F(0.0), infinities(0)
for _, v := range values {
switch {
- case math.IsInf(v, 1):
+ case math.IsInf(float64(v), 1):
infs |= posInf
- case math.IsInf(v, -1):
+ case math.IsInf(float64(v), -1):
infs |= negInf
}
sum += v
}
- return sum / float64(len(values)), infs
+ return F(sum / F(len(values))), infs
}
// infinities is a bitset that records the presence of ±Inf in the input
@@ -126,7 +126,7 @@
// -Inf is treated as smaller than all other values,
// Inf is treated as larger than all other values, and
// -0.0 is treated as smaller than 0.0.
-func Median(values []float64) float64 { return Quantiles(values, 0.5)[0] }
+func Median[F ~float64](values []F) F { return Quantiles(values, 0.5)[0] }
// Quantiles returns a sequence of quantiles of values.
//
@@ -148,7 +148,7 @@
// -Inf is treated as smaller than all other values,
// Inf is treated as larger than all other values, and
// -0.0 is treated as smaller than 0.0.
-func Quantiles(values []float64, quantiles ...float64) []float64 {
+func Quantiles[F ~float64](values []F, quantiles ...F) []F {
if len(values) == 0 {
panic("quantiles: empty slice")
}
@@ -156,10 +156,10 @@
values = slices.Clone(values)
slices.Sort(values)
}
- res := make([]float64, len(quantiles))
- if math.IsNaN(values[0]) {
+ res := make([]F, len(quantiles))
+ if math.IsNaN(float64(values[0])) {
for i := range res {
- res[i] = math.NaN()
+ res[i] = F(math.NaN())
}
return res
}
@@ -182,14 +182,14 @@
// over a sorted slice of vals.
//
// hyndmanFanR7 does not modify the array.
-func hyndmanFanR7(values []float64, q float64) float64 {
- h := float64(len(values)-1)*q + 1
+func hyndmanFanR7[F ~float64](values []F, q F) F {
+ h := F(len(values)-1)*q + 1
// the h-th smallest of len(vals) values is at fn(h)-1.
- return values[floor(h-1)] + (h-math.Floor(h))*(values[ceil(h-1)]-values[floor(h-1)])
+ return values[floor(h-1)] + (h-F(math.Floor(float64(h))))*(values[ceil(h-1)]-values[floor(h-1)])
}
// ceil returns the integer value of [math.Ceil].
-func ceil(n float64) int { return int(math.Ceil(n)) }
+func ceil[F ~float64](n F) int { return int(math.Ceil(float64(n))) }
// floor returns the integer value of [math.Floor].
-func floor(n float64) int { return int(math.Floor(n)) }
+func floor[F ~float64](n F) int { return int(math.Floor(float64(n))) }