diff --git a/finance.go b/finance.go new file mode 100644 index 0000000..02ca130 --- /dev/null +++ b/finance.go @@ -0,0 +1,50 @@ +package humanize + +import ( + "fmt" +) + +var ( + FinanceSign = "$" +) + +func Finance(f float64) string { + switch n := f; { + case n >= 1_000_000_000_000_000: + s := fmt.Sprintf("%.f", f) + s = insertAt(1, '.', []rune(s[:4])) + return fmt.Sprintf("%s%sQ", FinanceSign, s) + + case n >= 1_000_000_000_000: + s := fmt.Sprintf("%.f", f) + s = insertAt(1, '.', []rune(s[:4])) + return fmt.Sprintf("%s%sT", FinanceSign, s) + + case n >= 1_000_000_000: + s := fmt.Sprintf("%.f", f) + s = insertAt(1, '.', []rune(s[:4])) + return fmt.Sprintf("%s%sB", FinanceSign, s) + + case n >= 1_000_000: + s := fmt.Sprintf("%.f", f) + s = insertAt(1, '.', []rune(s[:4])) + return fmt.Sprintf("%s%sM", FinanceSign, s) + + case n >= 1_000: + s := fmt.Sprintf("%.f", f) + s = insertAt(1, '.', []rune(s[:4])) + return fmt.Sprintf("%s%sK", FinanceSign, s) + + case n < 1_000: + return fmt.Sprintf("%s%.f", FinanceSign, f) + + default: + return "NaN" + } +} + +func insertAt(index int, elem rune, slice []rune) string { + copy(slice[index+1:], slice[index:]) + slice[index] = elem + return string(slice) +} diff --git a/finance_test.go b/finance_test.go new file mode 100644 index 0000000..39f888d --- /dev/null +++ b/finance_test.go @@ -0,0 +1,30 @@ +package humanize + +import ( + "math" + "testing" +) + +func TestFinance(t *testing.T) { + tests := []struct { + name string + arg float64 + want string + }{ + {"Quadrillions", 2_475_260_494_216_000, "$2.47Q"}, + {"Trillions", 2_475_260_494_216, "$2.47T"}, + {"Billions", 2_475_260_494, "$2.47B"}, + {"Millions", 2_475_260, "$2.47M"}, + {"Thousands", 2_475, "$2.47K"}, + {"Hundreds", 247, "$247"}, + {"Tens", 24, "$24"}, + {"NaN", math.NaN(), "NaN"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Finance(tt.arg); got != tt.want { + t.Errorf("Finance() = %v, want %v", got, tt.want) + } + }) + } +}