Skip to content

Commit 0660d1b

Browse files
committedNov 21, 2016
Migrate from private stathat
1 parent d187e3a commit 0660d1b

File tree

2 files changed

+355
-0
lines changed

2 files changed

+355
-0
lines changed
 

‎numbers.go

+235
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
package numbers
2+
3+
import (
4+
"fmt"
5+
"math"
6+
"strconv"
7+
"strings"
8+
)
9+
10+
func Scale(num float64, digits int) float64 {
11+
if num == 0.0 {
12+
return 0.0
13+
}
14+
if math.IsNaN(num) {
15+
return 0.0
16+
}
17+
if math.IsInf(num, 0) {
18+
return num
19+
}
20+
21+
original := num
22+
num = math.Abs(num)
23+
24+
d := math.Ceil(math.Log10(num))
25+
power := digits - int(d)
26+
magnitude := math.Pow10(power)
27+
shifted := math.Ceil(num * magnitude)
28+
result := shifted / magnitude
29+
30+
result = math.Copysign(result, original)
31+
return result
32+
}
33+
34+
func SlideScale(num float64) float64 {
35+
if math.Abs(num) < 100.0 {
36+
return Scale(num, 1)
37+
}
38+
return Scale(num, 2)
39+
}
40+
41+
func ScaleDown(num float64, digits int) float64 {
42+
if num == 0.0 {
43+
return 0.0
44+
}
45+
if math.IsNaN(num) {
46+
return 0.0
47+
}
48+
if math.IsInf(num, 0) {
49+
return num
50+
}
51+
52+
original := num
53+
num = math.Abs(num)
54+
55+
result := 0.0
56+
57+
if num < 1.0 {
58+
result = math.Floor(num*10.0) / 10.0
59+
} else {
60+
d := math.Ceil(math.Log10(num))
61+
power := digits - int(d)
62+
magnitude := math.Pow10(power)
63+
shifted := math.Floor(num * magnitude)
64+
result = shifted / magnitude
65+
}
66+
67+
result = math.Copysign(result, original)
68+
return result
69+
}
70+
71+
func SlideScaleDown(num float64) float64 {
72+
if math.Abs(num) < 100.0 {
73+
return ScaleDown(num, 1)
74+
}
75+
return ScaleDown(num, 2)
76+
}
77+
78+
func CentsToDollars(cents float64) string {
79+
dollars := cents / 100.0
80+
return fmt.Sprintf("$%.2f", dollars)
81+
}
82+
83+
func AddDelimiters(num float64) string {
84+
s := strconv.FormatFloat(num, 'f', 3, 64)
85+
if num < 1000.0 {
86+
return s
87+
}
88+
pieces := strings.Split(s, ".")
89+
digits := []string(nil)
90+
91+
inum := pieces[0]
92+
x := len(inum)
93+
for x > 2 {
94+
digits = append(digits, inum[x-3:x])
95+
x -= 3
96+
}
97+
if x > 0 {
98+
digits = append(digits, inum[0:x])
99+
}
100+
101+
for i, j := 0, len(digits)-1; i < j; i, j = i+1, j-1 {
102+
digits[i], digits[j] = digits[j], digits[i]
103+
}
104+
105+
// drop the fraction
106+
// result := strings.Join(digits, ",") + "." + pieces[1]
107+
result := strings.Join(digits, ",")
108+
109+
return result
110+
}
111+
112+
func AddDelimitersInt(num int) string {
113+
inum := strconv.Itoa(num)
114+
digits := []string(nil)
115+
116+
x := len(inum)
117+
for x > 2 {
118+
digits = append(digits, inum[x-3:x])
119+
x -= 3
120+
}
121+
if x > 0 {
122+
digits = append(digits, inum[0:x])
123+
}
124+
125+
for i, j := 0, len(digits)-1; i < j; i, j = i+1, j-1 {
126+
digits[i], digits[j] = digits[j], digits[i]
127+
}
128+
129+
// drop the fraction
130+
// result := strings.Join(digits, ",") + "." + pieces[1]
131+
result := strings.Join(digits, ",")
132+
133+
return result
134+
}
135+
136+
func Display(num float64) string {
137+
if num >= 10000.0 {
138+
return Humanize(num)
139+
}
140+
if num >= 1000.0 {
141+
return AddDelimiters(num)
142+
}
143+
144+
if num > 1.0 && math.Floor(num) == num {
145+
return fmt.Sprintf("%.0f", num)
146+
}
147+
148+
if num == 0.0 {
149+
return "0"
150+
}
151+
152+
return fmt.Sprintf("%.3f", num)
153+
}
154+
155+
func Humanize(num float64) string {
156+
var exponent = 0.0
157+
if num != 0 {
158+
exponent = math.Floor(math.Log10(math.Abs(num)))
159+
}
160+
161+
if exponent >= 3 {
162+
unit := "K"
163+
dispExponent := 3
164+
if exponent >= 15 {
165+
unit = "Q"
166+
dispExponent = 15
167+
} else if exponent >= 12 {
168+
unit = "T"
169+
dispExponent = 12
170+
} else if exponent >= 9 {
171+
unit = "G"
172+
dispExponent = 9
173+
} else if exponent >= 6 {
174+
unit = "M"
175+
dispExponent = 6
176+
}
177+
num /= math.Pow10(dispExponent)
178+
return fmt.Sprintf("%.2f%s", num, unit)
179+
}
180+
181+
return AddDelimiters(num)
182+
}
183+
184+
func Words(num float64) string {
185+
var exponent = 0.0
186+
if num != 0 {
187+
exponent = math.Floor(math.Log10(math.Abs(num)))
188+
}
189+
190+
if exponent >= 3 {
191+
unit := "thousand"
192+
dispExponent := 3
193+
if exponent >= 15 {
194+
unit = "quadrillion"
195+
dispExponent = 15
196+
} else if exponent >= 12 {
197+
unit = "trillion"
198+
dispExponent = 12
199+
} else if exponent >= 9 {
200+
unit = "billion"
201+
dispExponent = 9
202+
} else if exponent >= 6 {
203+
unit = "million"
204+
dispExponent = 6
205+
}
206+
num /= math.Pow10(dispExponent)
207+
return fmt.Sprintf("%.2f %s", num, unit)
208+
}
209+
210+
return AddDelimiters(num)
211+
}
212+
213+
func Percentage(current, old float64) float64 {
214+
if old == 0.0 {
215+
return 0.0
216+
}
217+
return 100.0 * (current - old) / old
218+
}
219+
220+
func DisplayPercentage(percentage float64) string {
221+
return Display(percentage)
222+
}
223+
224+
// using midpoint method
225+
func PercentageMid(current, old float64) float64 {
226+
mid := 0.5 * (current + old)
227+
if mid == 0.0 {
228+
return 0.0
229+
}
230+
return 100.0 * (current - old) / mid
231+
}
232+
233+
func Megabytes(bytes uint64) float64 {
234+
return float64(bytes) / (1024.0 * 1024.0)
235+
}

‎numbers_test.go

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package numbers
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestAddDelim(t *testing.T) {
8+
s := AddDelimiters(32.3)
9+
if s != "32.300" {
10+
t.Errorf("expected 32.3, not '%s'", s)
11+
}
12+
13+
s = AddDelimiters(3032.3)
14+
if s != "3,032" {
15+
t.Errorf("expected 3,032.3000, not '%s'", s)
16+
}
17+
18+
s = AddDelimiters(123131231233032.3)
19+
if s != "123,131,231,233,032" {
20+
t.Errorf("expected 3,032.3000, not '%s'", s)
21+
}
22+
23+
s = AddDelimiters(1123131231233032.3)
24+
if s != "1,123,131,231,233,032" {
25+
t.Errorf("expected 3,032.300, not '%s'", s)
26+
}
27+
s = AddDelimiters(12123131231233032.3)
28+
if s != "12,123,131,231,233,032" {
29+
t.Errorf("expected 3,032.300, not '%s'", s)
30+
}
31+
32+
s = AddDelimitersInt(12123131231233032)
33+
if s != "12,123,131,231,233,032" {
34+
t.Errorf("expected 3,032.300, not '%s'", s)
35+
}
36+
}
37+
38+
type dtest struct {
39+
num float64
40+
result string
41+
}
42+
43+
var dispTests = []dtest{{999.0, "999"},
44+
{0.3145, "0.315"},
45+
{1300000, "1.30M"},
46+
{871300000, "871.30M"},
47+
{1300000000, "1.30G"},
48+
{13000000000, "13.00G"},
49+
{1300000000000, "1.30T"},
50+
{1300000000000000, "1.30Q"},
51+
{0.0, "0"},
52+
{0.0000, "0"},
53+
{1001.0, "1,001"}}
54+
55+
func TestDisplay(t *testing.T) {
56+
for i, v := range dispTests {
57+
s := Display(v.num)
58+
if s != v.result {
59+
t.Errorf("%d: expected '%s', got '%s'", i, v.result, s)
60+
}
61+
}
62+
}
63+
64+
func TestPercentage(t *testing.T) {
65+
if Percentage(100.0, 0.0) != 0.0 {
66+
t.Errorf("expected 0, got %f", Percentage(100.0, 0.0))
67+
}
68+
if PercentageMid(100.0, 0.0) != 200.0 {
69+
t.Errorf("expected 200%%, got %f", PercentageMid(100.0, 0.0))
70+
}
71+
if Percentage(110.0, 100.0) != 10.0 {
72+
t.Errorf("expected 10%%, got %f", Percentage(110.0, 100.0))
73+
}
74+
if PercentageMid(110.0, 100.0) != 9.523809523809524 {
75+
t.Errorf("expected 10%%, got %f", PercentageMid(110.0, 100.0))
76+
}
77+
}
78+
79+
func TestScale(t *testing.T) {
80+
if Scale(1.23, 1) != 2.0 {
81+
t.Errorf("expected 2.0, got %f", Scale(1.23, 1))
82+
}
83+
84+
if ScaleDown(1.23, 1) != 1.0 {
85+
t.Errorf("down scale to 1.0, got %f", ScaleDown(1.23, 1))
86+
}
87+
if ScaleDown(2.9, 1) != 2.0 {
88+
t.Errorf("down scale to 2.0, got %f", ScaleDown(2.9, 1))
89+
}
90+
if ScaleDown(2934.0, 1) != 2000.0 {
91+
t.Errorf("down scale to 2000.0, got %f", ScaleDown(2934.0, 1))
92+
}
93+
if Scale(2934.0, 1) != 3000.0 {
94+
t.Errorf("up scale to 3000.0, got %f", Scale(2934.0, 1))
95+
}
96+
if Scale(-2.3, 1) != -3.0 {
97+
t.Errorf("expected -3.0, got %f", Scale(-2.3, 1))
98+
}
99+
if Scale(0.25, 1) != 0.3 {
100+
t.Errorf("expected 0.3, got %f", Scale(0.25, 1))
101+
}
102+
if Scale(0.00003, 1) != 0.00003 {
103+
t.Errorf("expected 0.00003, got %f", Scale(0.00003, 1))
104+
}
105+
if Scale(0.000023, 1) != 0.00003 {
106+
t.Errorf("expected 0.00003, got %f", Scale(0.000023, 1))
107+
}
108+
if Scale(-0.000023, 1) != -0.00003 {
109+
t.Errorf("expected -0.00003, got %f", Scale(-0.000023, 1))
110+
}
111+
if Scale(6210.0, 2) != 6300.0 {
112+
t.Errorf("Scale(6210, 2) == %v, expected 6300.0", Scale(6210.0, 2))
113+
}
114+
if ScaleDown(6210.0, 2) != 6200.0 {
115+
t.Errorf("ScaleDown(6210, 2) == %v, expected 6200.0", ScaleDown(6210.0, 2))
116+
}
117+
if Scale(0.251, 2) != 0.26 {
118+
t.Errorf("scale 0.251, 2 = %v, expected 0.26", Scale(0.251, 2))
119+
}
120+
}

0 commit comments

Comments
 (0)