Skip to content

Commit 0dc6dc2

Browse files
authored
Optimize next highest power of two calculation (#737)
Refactored the logic to find the next highest power of two using bits.Len
1 parent e0d20dc commit 0dc6dc2

File tree

2 files changed

+95
-10
lines changed

2 files changed

+95
-10
lines changed

pkg/math/math.go

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
// Package math provides a few fast math functions.
1616
package math
1717

18+
import "math/bits"
19+
1820
const (
1921
bitSize = 32 << (^uint(0) >> 63)
2022
maxintHeadBit = 1 << (bitSize - 2)
@@ -34,16 +36,7 @@ func CeilToPowerOfTwo(n int) int {
3436
if n <= 2 {
3537
return 2
3638
}
37-
38-
n--
39-
n |= n >> 1
40-
n |= n >> 2
41-
n |= n >> 4
42-
n |= n >> 8
43-
n |= n >> 16
44-
n++
45-
46-
return n
39+
return 1 << bits.Len(uint(n-1))
4740
}
4841

4942
// FloorToPowerOfTwo returns n if it is a power-of-two, otherwise the next-highest power-of-two.

pkg/math/math_test.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package math
2+
3+
import "testing"
4+
5+
func TestCeilToPowerOfTwo(t *testing.T) {
6+
type args struct {
7+
n int
8+
}
9+
tests := []struct {
10+
name string
11+
args args
12+
want int
13+
}{
14+
// Boundary value tests: 0, 1, 2
15+
{name: "zero", args: args{n: 0}, want: 2},
16+
{name: "one", args: args{n: 1}, want: 2},
17+
{name: "two", args: args{n: 2}, want: 2},
18+
19+
// Small value tests: 3-15
20+
{name: "three", args: args{n: 3}, want: 1 << 2},
21+
{name: "four", args: args{n: 4}, want: 1 << 2},
22+
{name: "five", args: args{n: 5}, want: 1 << 3},
23+
{name: "six", args: args{n: 6}, want: 1 << 3},
24+
{name: "seven", args: args{n: 7}, want: 1 << 3},
25+
{name: "eight", args: args{n: 8}, want: 1 << 3},
26+
{name: "nine", args: args{n: 9}, want: 1 << 4},
27+
{name: "ten", args: args{n: 10}, want: 1 << 4},
28+
{name: "fifteen", args: args{n: 15}, want: 1 << 4},
29+
30+
// Tests for powers of two
31+
{name: "power_of_two_16", args: args{n: 1 << 4}, want: 1 << 4},
32+
{name: "power_of_two_32", args: args{n: 1 << 5}, want: 1 << 5},
33+
{name: "power_of_two_64", args: args{n: 1 << 6}, want: 1 << 6},
34+
{name: "power_of_two_128", args: args{n: 1 << 7}, want: 1 << 7},
35+
{name: "power_of_two_256", args: args{n: 1 << 8}, want: 1 << 8},
36+
{name: "power_of_two_512", args: args{n: 1 << 9}, want: 1 << 9},
37+
{name: "power_of_two_1024", args: args{n: 1 << 10}, want: 1 << 10},
38+
39+
// Values near powers of two
40+
{name: "near_power_17", args: args{n: (1 << 4) + 1}, want: 1 << 5},
41+
{name: "near_power_31", args: args{n: (1 << 5) - 1}, want: 1 << 5},
42+
{name: "near_power_33", args: args{n: (1 << 5) + 1}, want: 1 << 6},
43+
{name: "near_power_63", args: args{n: (1 << 6) - 1}, want: 1 << 6},
44+
{name: "near_power_65", args: args{n: (1 << 6) + 1}, want: 1 << 7},
45+
{name: "near_power_127", args: args{n: (1 << 7) - 1}, want: 1 << 7},
46+
{name: "near_power_129", args: args{n: (1 << 7) + 1}, want: 1 << 8},
47+
{name: "near_power_255", args: args{n: (1 << 8) - 1}, want: 1 << 8},
48+
{name: "near_power_257", args: args{n: (1 << 8) + 1}, want: 1 << 9},
49+
{name: "near_power_511", args: args{n: (1 << 9) - 1}, want: 1 << 9},
50+
{name: "near_power_513", args: args{n: (1 << 9) + 1}, want: 1 << 10},
51+
{name: "near_power_1023", args: args{n: (1 << 10) - 1}, want: 1 << 10},
52+
53+
// Medium value tests
54+
{name: "medium_100", args: args{n: 100}, want: 1 << 7},
55+
{name: "medium_200", args: args{n: 200}, want: 1 << 8},
56+
{name: "medium_500", args: args{n: 500}, want: 1 << 9},
57+
{name: "medium_1000", args: args{n: 1000}, want: 1 << 10},
58+
{name: "medium_2000", args: args{n: 2000}, want: 1 << 11},
59+
{name: "medium_5000", args: args{n: 5000}, want: 1 << 13},
60+
{name: "medium_10000", args: args{n: 10000}, want: 1 << 14},
61+
62+
// Large value tests: around 2^10
63+
{name: "large_1024_minus_1", args: args{n: 1<<10 - 1}, want: 1 << 10},
64+
{name: "large_1024", args: args{n: 1 << 10}, want: 1 << 10},
65+
{name: "large_1024_plus_1", args: args{n: 1<<10 + 1}, want: 1 << 11},
66+
{name: "large_2047", args: args{n: (1 << 11) - 1}, want: 1 << 11},
67+
{name: "large_2048", args: args{n: 1 << 11}, want: 1 << 11},
68+
{name: "large_2049", args: args{n: (1 << 11) + 1}, want: 1 << 12},
69+
70+
// Very large value tests: around 2^20
71+
{name: "very_large_1M_minus_1", args: args{n: 1<<20 - 1}, want: 1 << 20},
72+
{name: "very_large_1M", args: args{n: 1 << 20}, want: 1 << 20},
73+
{name: "very_large_1M_plus_1", args: args{n: 1<<20 + 1}, want: 1 << 21},
74+
75+
// Huge value tests: around 2^30 (32-bit system)
76+
{name: "huge_1G_minus_1", args: args{n: 1<<30 - 1}, want: 1 << 30},
77+
{name: "huge_1G", args: args{n: 1 << 30}, want: 1 << 30},
78+
{name: "huge_1G_plus_1", args: args{n: 1<<30 + 1}, want: 1 << 31},
79+
80+
// 64-bit system tests: around 2^32
81+
{name: "extreme_2_32_minus_1", args: args{n: 1<<32 - 1}, want: 1 << 32},
82+
{name: "extreme_2_32", args: args{n: 1 << 32}, want: 1 << 32},
83+
{name: "extreme_2_32_plus_1", args: args{n: 1<<32 + 1}, want: 1 << 33},
84+
}
85+
for _, tt := range tests {
86+
t.Run(tt.name, func(t *testing.T) {
87+
if got := CeilToPowerOfTwo(tt.args.n); got != tt.want {
88+
t.Errorf("CeilToPowerOfTwo() = %v, want %v", got, tt.want)
89+
}
90+
})
91+
}
92+
}

0 commit comments

Comments
 (0)