Skip to content

Commit 9d01def

Browse files
committed
math/bits: support negative rotation count and remove RotateRight
For details see the discussion on the issue below. RotateLeft functions can now be inlined because the don't panic anymore for negative rotation counts. name old time/op new time/op delta RotateLeft-8 6.72ns ± 2% 1.86ns ± 0% -72.33% (p=0.016 n=5+4) RotateLeft8-8 4.41ns ± 2% 1.67ns ± 1% -62.15% (p=0.008 n=5+5) RotateLeft16-8 4.46ns ± 6% 1.65ns ± 0% -63.06% (p=0.008 n=5+5) RotateLeft32-8 4.50ns ± 5% 1.67ns ± 1% -62.86% (p=0.008 n=5+5) RotateLeft64-8 4.54ns ± 1% 1.85ns ± 1% -59.32% (p=0.008 n=5+5) https://perf.golang.org/search?q=upload:20170411.4 (Measured on 2.3 GHz Intel Core i7 running macOS 10.12.3.) For #18616. Change-Id: I0828d80d54ec24f8d44954a57b3d6aeedb69c686 Reviewed-on: https://go-review.googlesource.com/40394 Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 927f8a0 commit 9d01def

File tree

2 files changed

+34
-157
lines changed

2 files changed

+34
-157
lines changed

src/math/bits/bits.go

+10-67
Original file line numberDiff line numberDiff line change
@@ -163,104 +163,47 @@ func OnesCount64(x uint64) int {
163163

164164
// --- RotateLeft ---
165165

166-
// RotateLeft returns the value of x rotated left by k bits; k must not be negative.
166+
// RotateLeft returns the value of x rotated left by (k mod UintSize) bits.
167+
// To rotate x right by k bits, call RotateLeft(x, -k).
167168
func RotateLeft(x uint, k int) uint {
168169
if UintSize == 32 {
169170
return uint(RotateLeft32(uint32(x), k))
170171
}
171172
return uint(RotateLeft64(uint64(x), k))
172173
}
173174

174-
// RotateLeft8 returns the value of x rotated left by k bits; k must not be negative.
175+
// RotateLeft8 returns the value of x rotated left by (k mod 8) bits.
176+
// To rotate x right by k bits, call RotateLeft8(x, -k).
175177
func RotateLeft8(x uint8, k int) uint8 {
176-
if k < 0 {
177-
panic("negative rotation count")
178-
}
179178
const n = 8
180179
s := uint(k) & (n - 1)
181180
return x<<s | x>>(n-s)
182181
}
183182

184-
// RotateLeft16 returns the value of x rotated left by k bits; k must not be negative.
183+
// RotateLeft16 returns the value of x rotated left by (k mod 16) bits.
184+
// To rotate x right by k bits, call RotateLeft16(x, -k).
185185
func RotateLeft16(x uint16, k int) uint16 {
186-
if k < 0 {
187-
panic("negative rotation count")
188-
}
189186
const n = 16
190187
s := uint(k) & (n - 1)
191188
return x<<s | x>>(n-s)
192189
}
193190

194-
// RotateLeft32 returns the value of x rotated left by k bits; k must not be negative.
191+
// RotateLeft32 returns the value of x rotated left by (k mod 32) bits.
192+
// To rotate x right by k bits, call RotateLeft32(x, -k).
195193
func RotateLeft32(x uint32, k int) uint32 {
196-
if k < 0 {
197-
panic("negative rotation count")
198-
}
199194
const n = 32
200195
s := uint(k) & (n - 1)
201196
return x<<s | x>>(n-s)
202197
}
203198

204-
// RotateLeft64 returns the value of x rotated left by k bits; k must not be negative.
199+
// RotateLeft64 returns the value of x rotated left by (k mod 64) bits.
200+
// To rotate x right by k bits, call RotateLeft64(x, -k).
205201
func RotateLeft64(x uint64, k int) uint64 {
206-
if k < 0 {
207-
panic("negative rotation count")
208-
}
209202
const n = 64
210203
s := uint(k) & (n - 1)
211204
return x<<s | x>>(n-s)
212205
}
213206

214-
// --- RotateRight ---
215-
216-
// RotateRight returns the value of x rotated left by k bits; k must not be negative.
217-
func RotateRight(x uint, k int) uint {
218-
if UintSize == 32 {
219-
return uint(RotateRight32(uint32(x), k))
220-
}
221-
return uint(RotateRight64(uint64(x), k))
222-
}
223-
224-
// RotateRight8 returns the value of x rotated left by k bits; k must not be negative.
225-
func RotateRight8(x uint8, k int) uint8 {
226-
if k < 0 {
227-
panic("negative rotation count")
228-
}
229-
const n = 8
230-
s := uint(k) & (n - 1)
231-
return x<<(n-s) | x>>s
232-
}
233-
234-
// RotateRight16 returns the value of x rotated left by k bits; k must not be negative.
235-
func RotateRight16(x uint16, k int) uint16 {
236-
if k < 0 {
237-
panic("negative rotation count")
238-
}
239-
const n = 16
240-
s := uint(k) & (n - 1)
241-
return x<<(n-s) | x>>s
242-
}
243-
244-
// RotateRight32 returns the value of x rotated left by k bits; k must not be negative.
245-
func RotateRight32(x uint32, k int) uint32 {
246-
if k < 0 {
247-
panic("negative rotation count")
248-
}
249-
const n = 32
250-
s := uint(k) & (n - 1)
251-
return x<<(n-s) | x>>s
252-
}
253-
254-
// RotateRight64 returns the value of x rotated left by k bits; k must not be negative.
255-
func RotateRight64(x uint64, k int) uint64 {
256-
if k < 0 {
257-
panic("negative rotation count")
258-
}
259-
const n = 64
260-
s := uint(k) & (n - 1)
261-
return x<<(n-s) | x>>s
262-
}
263-
264207
// --- Reverse ---
265208

266209
// Reverse returns the value of x with its bits in reversed order.

src/math/bits/bits_test.go

+24-90
Original file line numberDiff line numberDiff line change
@@ -342,27 +342,43 @@ func TestRotateLeft(t *testing.T) {
342342
if got8 != want8 {
343343
t.Fatalf("RotateLeft8(%#02x, %d) == %#02x; want %#02x", x8, k, got8, want8)
344344
}
345+
got8 = RotateLeft8(want8, -int(k))
346+
if got8 != x8 {
347+
t.Fatalf("RotateLeft8(%#02x, -%d) == %#02x; want %#02x", want8, k, got8, x8)
348+
}
345349

346350
x16 := uint16(m)
347351
got16 := RotateLeft16(x16, int(k))
348352
want16 := x16<<(k&0xf) | x16>>(16-k&0xf)
349353
if got16 != want16 {
350354
t.Fatalf("RotateLeft16(%#04x, %d) == %#04x; want %#04x", x16, k, got16, want16)
351355
}
356+
got16 = RotateLeft16(want16, -int(k))
357+
if got16 != x16 {
358+
t.Fatalf("RotateLeft16(%#04x, -%d) == %#04x; want %#04x", want16, k, got16, x16)
359+
}
352360

353361
x32 := uint32(m)
354362
got32 := RotateLeft32(x32, int(k))
355363
want32 := x32<<(k&0x1f) | x32>>(32-k&0x1f)
356364
if got32 != want32 {
357365
t.Fatalf("RotateLeft32(%#08x, %d) == %#08x; want %#08x", x32, k, got32, want32)
358366
}
367+
got32 = RotateLeft32(want32, -int(k))
368+
if got32 != x32 {
369+
t.Fatalf("RotateLeft32(%#08x, -%d) == %#08x; want %#08x", want32, k, got32, x32)
370+
}
359371
if UintSize == 32 {
360372
x := uint(m)
361373
got := RotateLeft(x, int(k))
362374
want := x<<(k&0x1f) | x>>(32-k&0x1f)
363375
if got != want {
364376
t.Fatalf("RotateLeft(%#08x, %d) == %#08x; want %#08x", x, k, got, want)
365377
}
378+
got = RotateLeft(want, -int(k))
379+
if got != x {
380+
t.Fatalf("RotateLeft(%#08x, -%d) == %#08x; want %#08x", want, k, got, x)
381+
}
366382
}
367383

368384
x64 := uint64(m)
@@ -371,13 +387,21 @@ func TestRotateLeft(t *testing.T) {
371387
if got64 != want64 {
372388
t.Fatalf("RotateLeft64(%#016x, %d) == %#016x; want %#016x", x64, k, got64, want64)
373389
}
390+
got64 = RotateLeft64(want64, -int(k))
391+
if got64 != x64 {
392+
t.Fatalf("RotateLeft64(%#016x, -%d) == %#016x; want %#016x", want64, k, got64, x64)
393+
}
374394
if UintSize == 64 {
375395
x := uint(m)
376396
got := RotateLeft(x, int(k))
377397
want := x<<(k&0x3f) | x>>(64-k&0x3f)
378398
if got != want {
379399
t.Fatalf("RotateLeft(%#016x, %d) == %#016x; want %#016x", x, k, got, want)
380400
}
401+
got = RotateLeft(want, -int(k))
402+
if got != x {
403+
t.Fatalf("RotateLeft(%#08x, -%d) == %#08x; want %#08x", want, k, got, x)
404+
}
381405
}
382406
}
383407
}
@@ -422,96 +446,6 @@ func BenchmarkRotateLeft64(b *testing.B) {
422446
Output = int(s)
423447
}
424448

425-
func TestRotateRight(t *testing.T) {
426-
var m uint64 = deBruijn64
427-
428-
for k := uint(0); k < 128; k++ {
429-
x8 := uint8(m)
430-
got8 := RotateRight8(x8, int(k))
431-
want8 := x8>>(k&0x7) | x8<<(8-k&0x7)
432-
if got8 != want8 {
433-
t.Fatalf("RotateRight8(%#02x, %d) == %#02x; want %#02x", x8, k, got8, want8)
434-
}
435-
436-
x16 := uint16(m)
437-
got16 := RotateRight16(x16, int(k))
438-
want16 := x16>>(k&0xf) | x16<<(16-k&0xf)
439-
if got16 != want16 {
440-
t.Fatalf("RotateRight16(%#04x, %d) == %#04x; want %#04x", x16, k, got16, want16)
441-
}
442-
443-
x32 := uint32(m)
444-
got32 := RotateRight32(x32, int(k))
445-
want32 := x32>>(k&0x1f) | x32<<(32-k&0x1f)
446-
if got32 != want32 {
447-
t.Fatalf("RotateRight32(%#08x, %d) == %#08x; want %#08x", x32, k, got32, want32)
448-
}
449-
if UintSize == 32 {
450-
x := uint(m)
451-
got := RotateRight(x, int(k))
452-
want := x>>(k&0x1f) | x<<(32-k&0x1f)
453-
if got != want {
454-
t.Fatalf("RotateRight(%#08x, %d) == %#08x; want %#08x", x, k, got, want)
455-
}
456-
}
457-
458-
x64 := uint64(m)
459-
got64 := RotateRight64(x64, int(k))
460-
want64 := x64>>(k&0x3f) | x64<<(64-k&0x3f)
461-
if got64 != want64 {
462-
t.Fatalf("RotateRight64(%#016x, %d) == %#016x; want %#016x", x64, k, got64, want64)
463-
}
464-
if UintSize == 64 {
465-
x := uint(m)
466-
got := RotateRight(x, int(k))
467-
want := x>>(k&0x3f) | x<<(64-k&0x3f)
468-
if got != want {
469-
t.Fatalf("RotateRight(%#016x, %d) == %#016x; want %#016x", x, k, got, want)
470-
}
471-
}
472-
}
473-
}
474-
475-
func BenchmarkRotateRight(b *testing.B) {
476-
var s uint
477-
for i := 0; i < b.N; i++ {
478-
s += RotateRight(uint(Input), i)
479-
}
480-
Output = int(s)
481-
}
482-
483-
func BenchmarkRotateRight8(b *testing.B) {
484-
var s uint8
485-
for i := 0; i < b.N; i++ {
486-
s += RotateRight8(uint8(Input), i)
487-
}
488-
Output = int(s)
489-
}
490-
491-
func BenchmarkRotateRight16(b *testing.B) {
492-
var s uint16
493-
for i := 0; i < b.N; i++ {
494-
s += RotateRight16(uint16(Input), i)
495-
}
496-
Output = int(s)
497-
}
498-
499-
func BenchmarkRotateRight32(b *testing.B) {
500-
var s uint32
501-
for i := 0; i < b.N; i++ {
502-
s += RotateRight32(uint32(Input), i)
503-
}
504-
Output = int(s)
505-
}
506-
507-
func BenchmarkRotateRight64(b *testing.B) {
508-
var s uint64
509-
for i := 0; i < b.N; i++ {
510-
s += RotateRight64(uint64(Input), i)
511-
}
512-
Output = int(s)
513-
}
514-
515449
func TestReverse(t *testing.T) {
516450
// test each bit
517451
for i := uint(0); i < 64; i++ {

0 commit comments

Comments
 (0)