-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexponential.go
100 lines (90 loc) · 2.42 KB
/
exponential.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package metric
import (
"fmt"
"math"
)
/*
exponential implements exponential-sized bin with range [-10^10, 10^10]
here uses (10th root of 10 = 1.258925412) as exponentail base, which means
bucket size will grow 10 times every 10 bins.
*/
const (
maxValue = 10000000000 // 10^10
minValue = -maxValue // -10^10
minFloatValue = 0.000001 // 10^-6
)
var (
// expCutoff stores upper bound of positive bin ids. Negative bin ids are
// symmetric to positive ones
expCutoff = map[int]float64{}
base = 0.1 // math.Log(10^0.1, 10) = 0.1
minLogV = math.Log10(minFloatValue) / base // log(10^-6, 10^0.1) = -60
maxLogV = math.Log10(maxValue) / base // log(10^10, 10^0.1) = 100
maxBin = int(maxLogV-minLogV+1) + 1
)
func init() {
i := 0
expCutoff[i] = 0
for i = 0; i < maxBin; i++ {
expCutoff[i+1] = math.Pow(10, (float64(i)+minLogV)*0.1)
}
// last bin stores value exceed maxValue
expCutoff[maxBin] = math.MaxFloat64
}
// exponential implements exponential-sized buckets with range [0, 10^8]
// here uses (10th root of 10 = 1.258925412) as exponentail base, which means
// bucket size will grow up 10 times every 10 buckets
type exponential struct {
}
// ValueRange returns min and max value
func (*exponential) ValueRange() (float64, float64) {
return minValue, maxValue
}
// BinRange returns min and max bin ID
func (*exponential) BinRange() (int, int) {
return -maxBin, maxBin
}
// Bin returns bin id the given value belong to
// def r = 10^0.1
// 61: [-1, -1/r)
// 11: [-10^-5, -10^-5/r)
// 2: [-r^2*10^-6, -r*10^-6)
// -1: [-10^-6, 0)
// 0: [0, 0]
// 1: (0, 10^-6]
// 2: (r*10^-6, r^2*10^-6]
// 11: (10^-5/r, 10^-5)
// 61: (1/r, 1]
func (*exponential) Bin(v float64) int {
if v >= 0 {
return bin(v)
}
return -bin(-v)
}
// Bound returns the upper and lower bound of given bin index
func (*exponential) Bound(bin int) (float64, float64) {
switch {
case bin > maxBin || bin < -maxBin:
panic(fmt.Errorf("index is out of range %d", bin))
case bin == 0:
return 0, 0
case bin > 0:
return expCutoff[bin-1], expCutoff[bin]
default:
bin = -bin
return -expCutoff[bin], -expCutoff[bin-1]
}
}
func bin(v float64) int {
// do log10(v) first to target a smaller bin range for binary search
switch {
case v == 0:
return 0
case v <= minFloatValue:
return 1
case v > maxValue:
return maxBin
default:
return int(math.Ceil(math.Log10(v)/base-minLogV)) + 1
}
}