Created
March 21, 2025 09:05
-
-
Save koblas/6e8ef10641237f33d97e825d56c03022 to your computer and use it in GitHub Desktop.
Benchmark of digit counting
This file contains hidden or 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
package main | |
import ( | |
"math" | |
"math/bits" | |
"strconv" | |
) | |
func LogCount(n int) int { | |
if n == 0 { | |
return 1 | |
} | |
return int(math.Log10(float64(n))) + 1 | |
} | |
func IntCount(n int) int { | |
cnt := 1 | |
for n > 9 { | |
n = n / 10 | |
cnt++ | |
} | |
return cnt | |
} | |
func StrCount(n int) int { | |
s := strconv.FormatInt(int64(n), 10) | |
return len(s) | |
} | |
func BitCount(n int) int { | |
if n == 0 { | |
return 1 | |
} | |
return (bits.Len(uint(n)) * 1233) >> 12 | |
} | |
func CaseCount(n int) int { | |
switch { | |
case n < 10: | |
return 1 | |
case n < 100: | |
return 2 | |
case n < 1000: | |
return 3 | |
case n < 10000: | |
return 4 | |
case n < 100000: | |
return 5 | |
case n < 1000000: | |
return 6 | |
case n < 10000000: | |
return 7 | |
case n < 100000000: | |
return 8 | |
case n < 1000000000: | |
return 9 | |
case n < 10000000000: | |
return 10 | |
case n < 100000000000: | |
return 11 | |
case n < 1000000000000: | |
return 12 | |
case n < 10000000000000: | |
return 13 | |
case n < 100000000000000: | |
return 14 | |
case n < 1000000000000000: | |
return 15 | |
case n < 10000000000000000: | |
return 16 | |
case n < 100000000000000000: | |
return 17 | |
case n < 1000000000000000000: | |
return 18 | |
} | |
return 19 | |
} | |
func TreeCount(n int) int { | |
if n < 10000 { // First major split | |
if n < 100 { // Second level split | |
if n < 10 { | |
return 1 | |
} | |
return 2 | |
} else { // n >= 100 | |
if n < 1000 { | |
return 3 | |
} | |
return 4 | |
} | |
} else if n < 100000000 { // First major split, second branch | |
if n < 1000000 { // Second level split | |
if n < 100000 { | |
return 5 | |
} | |
return 6 | |
} else { // n >= 1000000 | |
if n < 10000000 { | |
return 7 | |
} | |
return 8 | |
} | |
} else if n < 1000000000000 { // Third major branch | |
if n < 10000000000 { // Second level split | |
if n < 1000000000 { | |
return 9 | |
} | |
return 10 | |
} else { // n >= 10000000000 | |
if n < 100000000000 { | |
return 11 | |
} | |
return 12 | |
} | |
} else if n < 10000000000000000 { // Fourth major branch | |
if n < 100000000000000 { // Second level split | |
if n < 10000000000000 { | |
return 13 | |
} | |
return 14 | |
} else { // n >= 100000000000000 | |
if n < 1000000000000000 { | |
return 15 | |
} | |
return 16 | |
} | |
} else { // Fifth major branch, n >= 10000000000000000 | |
if n < 1000000000000000000 { // Second level split | |
if n < 100000000000000000 { | |
return 17 | |
} | |
return 18 | |
} else { // n >= 1000000000000000000 | |
return 19 // Covering the math.MaxInt64 case | |
} | |
} | |
} |
This file contains hidden or 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
package main | |
import ( | |
"math" | |
"testing" | |
) | |
func FuzzCount(f *testing.F) { | |
f.Fuzz(func(t *testing.T, n int) { | |
if n < 0 { | |
return | |
} | |
lc := LogCount(n) | |
if v := StrCount(n); lc != v { | |
t.Errorf("%d: StrCount expected=%d got=%d", n, lc, v) | |
} | |
if v := IntCount(n); lc != v { | |
t.Errorf("%d: IntCount expected=%d got=%d", n, lc, v) | |
} | |
// if v := BitCount(n); lc != v { | |
// t.Errorf("%d: BitCount expected=%d got=%d", n, lc, v) | |
// } | |
if v := CaseCount(n); lc != v { | |
t.Errorf("%d: CaseCount expected=%d got=%d", n, lc, v) | |
} | |
if v := TreeCount(n); lc != v { | |
t.Errorf("%d: TreeCount expected=%d got=%d", n, lc, v) | |
} | |
}) | |
} | |
var sizes = []struct { | |
key string | |
value int | |
}{ | |
{key: "zero", value: 0}, | |
{key: "one", value: 1}, | |
{key: "kilo", value: 1_000}, | |
{key: "mega", value: 1_000_000}, | |
{key: "giga", value: 1_000_000_000}, | |
{key: "max", value: math.MaxInt}, | |
} | |
func TestCount(t *testing.T) { | |
for _, item := range sizes { | |
t.Run(item.key, func(t *testing.T) { | |
n := item.value | |
lc := LogCount(n) | |
if v := StrCount(n); lc != v { | |
t.Errorf("%d: StrCount expected=%d got=%d", n, lc, v) | |
} | |
if v := IntCount(n); lc != v { | |
t.Errorf("%d: IntCount expected=%d got=%d", n, lc, v) | |
} | |
// if v := BitCount(n); lc != v { | |
// t.Errorf("%d: BitCount expected=%d got=%d", n, lc, v) | |
// } | |
if v := CaseCount(n); lc != v { | |
t.Errorf("%d: CaseCount expected=%d got=%d", n, lc, v) | |
} | |
if v := TreeCount(n); lc != v { | |
t.Errorf("%d: TreeCount expected=%d got=%d", n, lc, v) | |
} | |
}) | |
} | |
} | |
func BenchmarkLog(b *testing.B) { | |
for _, item := range sizes { | |
b.Run(item.key, func(b *testing.B) { | |
for b.Loop() { | |
LogCount(item.value) | |
} | |
}) | |
} | |
} | |
func BenchmarkInt(b *testing.B) { | |
for _, item := range sizes { | |
b.Run(item.key, func(b *testing.B) { | |
for b.Loop() { | |
IntCount(item.value) | |
} | |
}) | |
} | |
} | |
func BenchmarkStr(b *testing.B) { | |
for _, item := range sizes { | |
b.Run(item.key, func(b *testing.B) { | |
for b.Loop() { | |
StrCount(item.value) | |
} | |
}) | |
} | |
} | |
func BenchmarkCase(b *testing.B) { | |
for _, item := range sizes { | |
b.Run(item.key, func(b *testing.B) { | |
for b.Loop() { | |
CaseCount(item.value) | |
} | |
}) | |
} | |
} | |
func BenchmarkTree(b *testing.B) { | |
for _, item := range sizes { | |
b.Run(item.key, func(b *testing.B) { | |
for b.Loop() { | |
TreeCount(item.value) | |
} | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For reference the results: