我正在看《 A Tour of Go》,我对他们的basic-types.go示例感到困惑:
1 | MaxInt uint64 = 1<<64 - 1 |
难道不应该在无符号的64位整数中向左移动1 64个位置会导致溢出(也就是向MSB上方移动一点)吗?
但是,直到该行更改为:编译器才会抱怨:
1 2 3 | MaxInt uint64 = 1<<65 - 1 ./basic-types.go:5: constant 36893488147419103231 overflows uint64 |
如果我编写一些代码来迭代不同长度的左移,包括如上例中那样导致导致编译器发声的左移65,我会看到两件事:
它的行为符合我的预期,因为
它不再溢出了(呵呵!!!!)
码:
1 2 3 4 5 6 7 8 9 10 11 | package main import"fmt" func main() { for i := 60; i < 66; i++ { var j uint64 = 1 << uint64(i) - 1 fmt.Printf("%2d | %64b | %#18x\ ", i, j, j) } |
输出:
1 2 3 4 5 6 | 60 | 111111111111111111111111111111111111111111111111111111111111 | 0xfffffffffffffff 61 | 1111111111111111111111111111111111111111111111111111111111111 | 0x1fffffffffffffff 62 | 11111111111111111111111111111111111111111111111111111111111111 | 0x3fffffffffffffff 63 | 111111111111111111111111111111111111111111111111111111111111111 | 0x7fffffffffffffff 64 | 1111111111111111111111111111111111111111111111111111111111111111 | 0xffffffffffffffff 65 | 1111111111111111111111111111111111111111111111111111111111111111 | 0xffffffffffffffff |
当你写
1 | 1<<64 |
上面的
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.
因此,常量文字会在编译时进行评估,因为它不是语言实现的特定类型,所以可能非常大。
下面实际上会给出一个溢出错误:
1 2 | var i int64 i = 1<<65 - 1 |
因为现在常量文字表达式的计算结果大于
在这里阅读更多有关此的内容。
要知道为什么示例代码适用于
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.
上面的膨胀部分与您的代码有关。考虑下面的代码:
1 2 | a := 66 var j uint64 = 1<<uint64(a) - 1 |
在移位运算符中,右操作数是一个非恒定的表达式。因此整个移位操作成为非恒定移位表达式。因此,如上所述,左操作数
现在,在
请注意,根据语言规范,此行为与溢出并不相同。同样,只要正确的运算符不是一个常量表达式,语言限制就可以进行任意多的移位。因此,例如,这将起作用:
1 2 | a := 6666 var j uint64 = 1<<uint64(a) - 1 // non-constant shift expression |
这样想吧。以前,
这仍然会导致溢出,因为左操作数
1 2 | var j uint64 = 1<<uint64(66) - 1 // overflow. Note that uint64(64) fmt.Println(j) // is typed, but it's still a constant |
-
很有意思,因此在
MaxInt uint64 = 1<<64 - 1 中,1<<64 在编译时以无类型的方式求值到18446744073709551616,然后在考虑到- 1 时,最终将其放入uint64中。 -
那么为什么当i = 65时循环中不会溢出?
var j uint64 = 1 << uint64(i) - 1 - 添加了解释。
- 有趣; 感谢那! 自从Ive必须执行按位运算以来已经有很长时间了,那时它在FPGA和ASIC中。 从我的实现角度来看,这是有意义的,它是一个不错的小功能,可以在编译时对常量文字进行任意精度的计算,因此您可以确切地知道常量的含义。