Skip to content

Commit

Permalink
Migrate from private stathat
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickxb committed Nov 21, 2016
1 parent d187e3a commit 0660d1b
Show file tree
Hide file tree
Showing 2 changed files with 355 additions and 0 deletions.
235 changes: 235 additions & 0 deletions numbers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
package numbers

import (
"fmt"
"math"
"strconv"
"strings"
)

func Scale(num float64, digits int) float64 {
if num == 0.0 {
return 0.0
}
if math.IsNaN(num) {
return 0.0
}
if math.IsInf(num, 0) {
return num
}

original := num
num = math.Abs(num)

d := math.Ceil(math.Log10(num))
power := digits - int(d)
magnitude := math.Pow10(power)
shifted := math.Ceil(num * magnitude)
result := shifted / magnitude

result = math.Copysign(result, original)
return result
}

func SlideScale(num float64) float64 {
if math.Abs(num) < 100.0 {
return Scale(num, 1)
}
return Scale(num, 2)
}

func ScaleDown(num float64, digits int) float64 {
if num == 0.0 {
return 0.0
}
if math.IsNaN(num) {
return 0.0
}
if math.IsInf(num, 0) {
return num
}

original := num
num = math.Abs(num)

result := 0.0

if num < 1.0 {
result = math.Floor(num*10.0) / 10.0
} else {
d := math.Ceil(math.Log10(num))
power := digits - int(d)
magnitude := math.Pow10(power)
shifted := math.Floor(num * magnitude)
result = shifted / magnitude
}

result = math.Copysign(result, original)
return result
}

func SlideScaleDown(num float64) float64 {
if math.Abs(num) < 100.0 {
return ScaleDown(num, 1)
}
return ScaleDown(num, 2)
}

func CentsToDollars(cents float64) string {
dollars := cents / 100.0
return fmt.Sprintf("$%.2f", dollars)
}

func AddDelimiters(num float64) string {
s := strconv.FormatFloat(num, 'f', 3, 64)
if num < 1000.0 {
return s
}
pieces := strings.Split(s, ".")
digits := []string(nil)

inum := pieces[0]
x := len(inum)
for x > 2 {
digits = append(digits, inum[x-3:x])
x -= 3
}
if x > 0 {
digits = append(digits, inum[0:x])
}

for i, j := 0, len(digits)-1; i < j; i, j = i+1, j-1 {
digits[i], digits[j] = digits[j], digits[i]
}

// drop the fraction
// result := strings.Join(digits, ",") + "." + pieces[1]
result := strings.Join(digits, ",")

return result
}

func AddDelimitersInt(num int) string {
inum := strconv.Itoa(num)
digits := []string(nil)

x := len(inum)
for x > 2 {
digits = append(digits, inum[x-3:x])
x -= 3
}
if x > 0 {
digits = append(digits, inum[0:x])
}

for i, j := 0, len(digits)-1; i < j; i, j = i+1, j-1 {
digits[i], digits[j] = digits[j], digits[i]
}

// drop the fraction
// result := strings.Join(digits, ",") + "." + pieces[1]
result := strings.Join(digits, ",")

return result
}

func Display(num float64) string {
if num >= 10000.0 {
return Humanize(num)
}
if num >= 1000.0 {
return AddDelimiters(num)
}

if num > 1.0 && math.Floor(num) == num {
return fmt.Sprintf("%.0f", num)
}

if num == 0.0 {
return "0"
}

return fmt.Sprintf("%.3f", num)
}

func Humanize(num float64) string {
var exponent = 0.0
if num != 0 {
exponent = math.Floor(math.Log10(math.Abs(num)))
}

if exponent >= 3 {
unit := "K"
dispExponent := 3
if exponent >= 15 {
unit = "Q"
dispExponent = 15
} else if exponent >= 12 {
unit = "T"
dispExponent = 12
} else if exponent >= 9 {
unit = "G"
dispExponent = 9
} else if exponent >= 6 {
unit = "M"
dispExponent = 6
}
num /= math.Pow10(dispExponent)
return fmt.Sprintf("%.2f%s", num, unit)
}

return AddDelimiters(num)
}

func Words(num float64) string {
var exponent = 0.0
if num != 0 {
exponent = math.Floor(math.Log10(math.Abs(num)))
}

if exponent >= 3 {
unit := "thousand"
dispExponent := 3
if exponent >= 15 {
unit = "quadrillion"
dispExponent = 15
} else if exponent >= 12 {
unit = "trillion"
dispExponent = 12
} else if exponent >= 9 {
unit = "billion"
dispExponent = 9
} else if exponent >= 6 {
unit = "million"
dispExponent = 6
}
num /= math.Pow10(dispExponent)
return fmt.Sprintf("%.2f %s", num, unit)
}

return AddDelimiters(num)
}

func Percentage(current, old float64) float64 {
if old == 0.0 {
return 0.0
}
return 100.0 * (current - old) / old
}

func DisplayPercentage(percentage float64) string {
return Display(percentage)
}

// using midpoint method
func PercentageMid(current, old float64) float64 {
mid := 0.5 * (current + old)
if mid == 0.0 {
return 0.0
}
return 100.0 * (current - old) / mid
}

func Megabytes(bytes uint64) float64 {
return float64(bytes) / (1024.0 * 1024.0)
}
120 changes: 120 additions & 0 deletions numbers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package numbers

import (
"testing"
)

func TestAddDelim(t *testing.T) {
s := AddDelimiters(32.3)
if s != "32.300" {
t.Errorf("expected 32.3, not '%s'", s)
}

s = AddDelimiters(3032.3)
if s != "3,032" {
t.Errorf("expected 3,032.3000, not '%s'", s)
}

s = AddDelimiters(123131231233032.3)
if s != "123,131,231,233,032" {
t.Errorf("expected 3,032.3000, not '%s'", s)
}

s = AddDelimiters(1123131231233032.3)
if s != "1,123,131,231,233,032" {
t.Errorf("expected 3,032.300, not '%s'", s)
}
s = AddDelimiters(12123131231233032.3)
if s != "12,123,131,231,233,032" {
t.Errorf("expected 3,032.300, not '%s'", s)
}

s = AddDelimitersInt(12123131231233032)
if s != "12,123,131,231,233,032" {
t.Errorf("expected 3,032.300, not '%s'", s)
}
}

type dtest struct {
num float64
result string
}

var dispTests = []dtest{{999.0, "999"},
{0.3145, "0.315"},
{1300000, "1.30M"},
{871300000, "871.30M"},
{1300000000, "1.30G"},
{13000000000, "13.00G"},
{1300000000000, "1.30T"},
{1300000000000000, "1.30Q"},
{0.0, "0"},
{0.0000, "0"},
{1001.0, "1,001"}}

func TestDisplay(t *testing.T) {
for i, v := range dispTests {
s := Display(v.num)
if s != v.result {
t.Errorf("%d: expected '%s', got '%s'", i, v.result, s)
}
}
}

func TestPercentage(t *testing.T) {
if Percentage(100.0, 0.0) != 0.0 {
t.Errorf("expected 0, got %f", Percentage(100.0, 0.0))
}
if PercentageMid(100.0, 0.0) != 200.0 {
t.Errorf("expected 200%%, got %f", PercentageMid(100.0, 0.0))
}
if Percentage(110.0, 100.0) != 10.0 {
t.Errorf("expected 10%%, got %f", Percentage(110.0, 100.0))
}
if PercentageMid(110.0, 100.0) != 9.523809523809524 {
t.Errorf("expected 10%%, got %f", PercentageMid(110.0, 100.0))
}
}

func TestScale(t *testing.T) {
if Scale(1.23, 1) != 2.0 {
t.Errorf("expected 2.0, got %f", Scale(1.23, 1))
}

if ScaleDown(1.23, 1) != 1.0 {
t.Errorf("down scale to 1.0, got %f", ScaleDown(1.23, 1))
}
if ScaleDown(2.9, 1) != 2.0 {
t.Errorf("down scale to 2.0, got %f", ScaleDown(2.9, 1))
}
if ScaleDown(2934.0, 1) != 2000.0 {
t.Errorf("down scale to 2000.0, got %f", ScaleDown(2934.0, 1))
}
if Scale(2934.0, 1) != 3000.0 {
t.Errorf("up scale to 3000.0, got %f", Scale(2934.0, 1))
}
if Scale(-2.3, 1) != -3.0 {
t.Errorf("expected -3.0, got %f", Scale(-2.3, 1))
}
if Scale(0.25, 1) != 0.3 {
t.Errorf("expected 0.3, got %f", Scale(0.25, 1))
}
if Scale(0.00003, 1) != 0.00003 {
t.Errorf("expected 0.00003, got %f", Scale(0.00003, 1))
}
if Scale(0.000023, 1) != 0.00003 {
t.Errorf("expected 0.00003, got %f", Scale(0.000023, 1))
}
if Scale(-0.000023, 1) != -0.00003 {
t.Errorf("expected -0.00003, got %f", Scale(-0.000023, 1))
}
if Scale(6210.0, 2) != 6300.0 {
t.Errorf("Scale(6210, 2) == %v, expected 6300.0", Scale(6210.0, 2))
}
if ScaleDown(6210.0, 2) != 6200.0 {
t.Errorf("ScaleDown(6210, 2) == %v, expected 6200.0", ScaleDown(6210.0, 2))
}
if Scale(0.251, 2) != 0.26 {
t.Errorf("scale 0.251, 2 = %v, expected 0.26", Scale(0.251, 2))
}
}

0 comments on commit 0660d1b

Please sign in to comment.