Skip to content

Add CommafWithMinDigits #96

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions comma.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ func CommafWithDigits(f float64, decimals int) string {
return stripTrailingDigits(Commaf(f), decimals)
}


// CommafWithMinDigits works like the Commaf but clamps the resulting
// string to the given number of decimal places, filling any missing
// decimal places with 0. This is useful for things like formatting money.
//
// e.g. CommafWithMinDigits(834142.3, 2) -> 834,142.30
func CommafWithMinDigits(f float64, decimals int) string {
return clampTrailingDigits(Commaf(f), decimals)
}

// BigComma produces a string form of the given big.Int in base 10
// with commas after every three orders of magnitude.
func BigComma(b *big.Int) string {
Expand Down
13 changes: 13 additions & 0 deletions comma_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,19 @@ func TestCommafWithDigits(t *testing.T) {
}.validate(t)
}

func TestCommafWithMinDigits(t *testing.T) {
testList{
{"1.2, 0", CommafWithMinDigits(1, 0), "1"},
{"1.2, 0", CommafWithMinDigits(1, 1), "1.0"},
{"1.2, 0", CommafWithMinDigits(1, 2), "1.00"},
{"1.2, 0", CommafWithMinDigits(1, 3), "1.000"},
{"1.2, 0", CommafWithMinDigits(1.2, 0), "1"},
{"1.2, 1", CommafWithMinDigits(1.2, 1), "1.2"},
{"1.2, 2", CommafWithMinDigits(1.2, 2), "1.20"},
{"1.2, 3", CommafWithMinDigits(1.2, 3), "1.200"},
}.validate(t)
}

func TestCommafs(t *testing.T) {
testList{
{"0", Commaf(0), "0"},
Expand Down
28 changes: 28 additions & 0 deletions ftoa.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,34 @@ func stripTrailingDigits(s string, digits int) string {
return s
}

func clampTrailingDigits(s string, digits int) string {
i := strings.Index(s, ".")

// short circuit. if there is no decimal separator, add one,
// and the appropriate number of zeros
if i == -1 {
// second short circuit - if they request <= 0 digits, just
// return the string, as it already doesn't have any digits.
if digits <= 0 {
return s
}
return s + "." + strings.Repeat("0", digits)
}

// third short circuit - if they request <= 0 digits, remove
// all digits and dot
if digits <= 0 {
return s[:i]
}

currentDigitLength := len(s)-i-1
if digits <= currentDigitLength {
return stripTrailingDigits(s,digits)
}

return s + strings.Repeat("0",digits-currentDigitLength)
}

// Ftoa converts a float to a string with no trailing zeros.
func Ftoa(num float64) string {
return stripTrailingZeros(strconv.FormatFloat(num, 'f', 6, 64))
Expand Down
59 changes: 59 additions & 0 deletions ftoa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,65 @@ func TestStripTrailingDigits(t *testing.T) {
}
}


func TestClampTrailingDigits(t *testing.T) {
err := quick.Check(func(s string, digits int) bool {
clamped := clampTrailingDigits(s, digits)

fmt.Println(s,digits,clamped)

if strings.ContainsRune(s, '.') {
// If there is a dot, the part on the left of the dot will never change
a := strings.Split(s, ".")
b := strings.Split(clamped, ".")
if a[0] != b[0] {
return false
}
} else {
// If there's no dot in the input, the output will never be the same as the input
// if the number of digits is > 0.
if digits != 0 && clamped == s {
return false
}
// If there's no dot in the input, the output will always be the same as the input
// if the number of digits is 0.
if digits == 0 && clamped != s {
return false
}
}

return true
}, &quick.Config{
MaxCount: 10000,
Values: func(v []reflect.Value, r *rand.Rand) {
rdigs := func(n int) string {
digs := []rune{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}
var rv []rune
for i := 0; i < n; i++ {
rv = append(rv, digs[r.Intn(len(digs))])
}
return string(rv)
}

ls := r.Intn(20)
rs := r.Intn(20)
jc := "."
if rs == 0 {
jc = ""
}
s := rdigs(ls) + jc + rdigs(rs)
digits := r.Intn(len(s) + 1)

v[0] = reflect.ValueOf(s)
v[1] = reflect.ValueOf(digits)
},
})

if err != nil {
t.Error(err)
}
}

func BenchmarkFtoaRegexTrailing(b *testing.B) {
trailingZerosRegex := regexp.MustCompile(`\.?0+$`)

Expand Down