-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
355 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} | ||
} |