Why doesn't left bit shifting by 64 overflow in golang?

倾然丶 夕夏残阳落幕 提交于 2019-12-05 10:54:57

When you write

1<<64

The 1 above is not an int64. It is a constant literal. From the language specs:

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.

Thus, a constant literal is evaluated at compile time and can be very large because it is not a specific type of the language implementation.

Below will in fact give an overflow error:

var i int64
i = 1<<65 - 1

Because now the constant literal expression evaluates to a value greater than that an int64 can contain.

Read more about this here.

To know why your example code works for i = 65, refer to the below specification from the Golang specs:

The right operand in a shift expression must have unsigned integer type or be an untyped constant that can be converted to unsigned integer type. If the left operand of a non-constant shift expression is an untyped constant, it is first converted to the type it would assume if the shift expression were replaced by its left operand alone.

The blod part above concerns your code. Consider the code below:

a := 66
var j uint64 = 1<<uint64(a) - 1

Here in the shift operator, the right operand is a non-constant exrpession. So the whole shift operation becomes non-constant shift expression. Thus, as described above, the left operand, 1 is converted to a uint64.

Now, the shift is being done on uint64(1), which can be shifted using << to as many places as you want. You can shift it beyond 64 bits, and the implementation will easily allow it. But in this case the memory that's holding the uint64(1) above will contain all zeros.

Note that this behavior is not the same thing as an overflow as per the language specifications. Again, the language implemntation allows for as many shifts as long as the right operator is not a constant expression. So for example, this will work:

a := 6666
var j uint64 = 1<<uint64(a) - 1 // non-constant shift expression

Think about it this way. Earlier, the 1 was untyped. It had an arbitrary precision (implementation dependent) and the whole number was being returned (all the bits). Now, since it is a uint64, only the first 64 bits are being taken into consideration.

This still causes an overflow because the left operand 1 is untypes and can contain a large number of bits, returning a value too large for a uint64:

var j uint64 = 1<<uint64(66) - 1 // overflow. Note that uint64(64)
fmt.Println(j)                   // is typed, but it's still a constant
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!