How does 1 << 64 - 1 work?

That is because the Go compiler handles constant expressions as numeric constants. Contrary to the data-types that have to obey the law of range, storage bits and side-effects like overflow, numeric constants never lose precision.

Numeric constants only get deduced down to a data-type with limited precision and range at the moment when you assign them to a variable (which has a known type and thus a numeric range and a defined way to store the number into bits). You can also force them to get deduced to a ordinary data-type by using them as part of a equation that contains non Numeric constant types.

Confused? So was I..

Here is a longer write-up on the data-types and how constants are handled: http://www.goinggo.net/2014/04/introduction-to-numeric-constants-in-go.html?m=1


I decided to try it. For reasons that are subtle, executing the expression as a constant expression (1 << 64 -1) or piece by piece at run time gives the same answer. This is because of 2 different mechanisms. A constant expression is fully evaluated with infinite precision before being assigned to the variable. The step by step execution explicitly allows overflows and underflows through addition, subtraction and shift operations, and thus the result is the same.

See https://golang.org/ref/spec#Integer_overflow for a description of how integers are supposed to overflow.

However, doing it in groups, ie 1<<64 and then -1 causes overflow errors!

You can make a variable overflow though arithmetic, but you can not assign an overflow to a variable.

Try it yourself. Paste the code below into http://try.golang.org/

This one works:

// You can edit this code!
// Click here and start typing.
package main

import "fmt"

func main() {
  var MaxInt uint64 = 1
  MaxInt = MaxInt << 64
  MaxInt = MaxInt  - 1
  fmt.Println("%d",MaxInt)
}

This one doesn't work:

// You can edit this code!
// Click here and start typing.
package main

import "fmt"

func main() {
  var MaxInt uint64 = 1 << 64
  MaxInt = MaxInt  - 1
  fmt.Println("%d",MaxInt)
}

The result of your expression is a (compile time) constant and the expression is therefore evaluated during compilation. The language specification mandates that

Constant expressions are always evaluated exactly; intermediate values and the constants themselves may require precision significantly larger than supported by any predeclared type in the language. The following are legal declarations:

const Huge = 1 << 100         // Huge == 1267650600228229401496703205376  (untyped integer constant)
const Four int8 = Huge >> 98  // Four == 4                                (type int8)

https://golang.org/ref/spec#Constant_expressions

Tags:

Bit Shift

Go