0

see code like this:

bitSize := 64
maxVal := uint64(1)<<uint(bitSize) - 1
fmt.Println(maxVal) // 18446744073709551615

the code has no problem, will print 18446744073709551615

but this will panic, why?

fmt.Println(uint64(1)<<uint(64)-1) // constant 18446744073709551616 overflows uint64

Thanks a lot.

littlec
  • 77
  • 3

1 Answers1

2

Your first case "works" due to integer overflow.

Because you are using uint(bitSize), the expression is no longer a constant and is instead evaluated at runtime.

Breaking it down, this yields:

  • uint64(1): that's just 1
  • 1 << 64: (still a uint64 since 1 is a uint64): 2^64 == 0 in uint64.
  • 0 - 1: 2^64 - 1 (18446744073709551615) due to unsigned integer wrap around.

If you change the expression to a constant (by replacing uint(bitSize) with uint(64), it will fail at compile time.


In the second example, you are asking the compiler to specifically treat 1 as a uint64. It will try to compute the constant 1 << 64 and store it in a uint64, but this is not possible without overflowing.

Instead, you should let the compiler operate on untyped constants and only force the final result to be uint64 (which fits):

fmt.Println(uint64(1<<64 - 1))

This works because shifts on untyped integer constants are also untyped integer constants. The entire constant expression can be evaluated to 2^64 - 1 before trying to store it in a uint64. The spec on constant expressions has the following to say:

If the left operand of a constant shift expression is an untyped constant, the result is an integer constant; otherwise it is a constant of the same type as the left operand, which must be of integer type.

Marc
  • 19,394
  • 6
  • 47
  • 51