我无法理解 golang 中的方式 1<<s
返回 0
如果var s uint = 33
.
但是1<<33
返回 8589934592
.
移位运算符转换如何以值 0 结束。
我正在阅读语言规范并卡在了这一部分: https://golang.org/ref/spec#Operators
特别是文档中的这一段:
"The right operand in a shift expression must have unsigned integer type or be an untyped constant representable by a value of type uint. If the left operand of a non-constant shift expression is an untyped constant, it is first implicitly converted to the type it would assume if the shift expression were replaced by its left operand alone."
来自官方 Golang 文档的一些示例:
var s uint = 33
var i = 1<<s // 1 has type int
var j int32 = 1<<s // 1 has type int32; j == 0
var k = uint64(1<<s) // 1 has type uint64; k == 1<<33
更新:
另一个非常相关的问题,有一个例子:
package main
import (
"fmt"
)
func main() {
v := int16(4336)
fmt.Println(int8(v))
}
这个程序返回-16
数字4336
怎么来的?成为-16
在转换int16
至 int8
最佳答案
如果你有这个:
var s uint = 33
fmt.Println(1 << s)
然后引用的部分适用:
If the left operand of a non-constant shift expression is an untyped constant, it is first implicitly converted to the type it would assume if the shift expression were replaced by its left operand alone.
因为 s
不是常量(它是变量),因此 1 >> s
是一个非常量移位表达式。左操作数是1
这是一个无类型的常量(例如 int(1)
将是一个类型化的常量),因此它被转换为如果表达式只是 1
时它会得到的类型。而不是 1 << s
:
fmt.Println(1)
在上面,无类型常量1
将转换为 int
,因为这是它的默认类型。常量的默认类型在 Spec: Constants: 中
An untyped constant has a default type which is the type to which the constant is implicitly converted in contexts where a typed value is required, for instance, in a short variable declaration such as
i := 0
where there is no explicit type. The default type of an untyped constant isbool
,rune
,int
,float64
,complex128
orstring
respectively, depending on whether it is a boolean, rune, integer, floating-point, complex, or string constant.
上面的结果是依赖于架构的。如果int
是 32 位,它将是 0
.如果int
是 64 位,它将是 8589934592
(因为将 1
位移动 33 次会将其移出 32 位 int
数字)。
在围棋 Playground 上,大小为 int
是 32 位(4 字节)。看这个例子:
fmt.Println("int size:", unsafe.Sizeof(int(0)))
var s uint = 33
fmt.Println(1 << s)
fmt.Println(int32(1) << s)
fmt.Println(int64(1) << s)
以上输出(在 Go Playground 上尝试):
int size: 4
0
0
8589934592
如果我在我的 64 位计算机上运行上面的应用程序,输出是:
int size: 8
8589934592
0
8589934592
另见 The Go Blog: Constants了解常量在 Go 中的工作原理。
注意如果你写1 << 33
,那是不一样的,那不是一个非常量移位表达式,您的引用适用于:“非常量移位表达式的左操作数”。 1<<33
是一个常量移位表达式,在“常量空间”进行计算,结果将转换为 int
不适合 32 位 int
,因此编译时错误。它适用于变量,因为变量可能会溢出。常量不会溢出:
Numeric constants represent exact values of arbitrary precision and do not overflow.
参见 How does Go perform arithmetic on constants?
更新:
回答您的添加:从 int16
转换至 int8
只保留最低的 8 位。整数使用 2's complement 表示格式,其中最高位为 1
如果数字是负数。
这在 Spec: Conversions: 中有详细说明
When converting between integer types, if the value is a signed integer, it is sign extended to implicit infinite precision; otherwise it is zero extended. It is then truncated to fit in the result type's size. For example, if
v := uint16(0x10F0)
, thenuint32(int8(v)) == 0xFFFFFFF0
. The conversion always yields a valid value; there is no indication of overflow.
所以当你转换一个 int16
值 int8
, 如果源编号有 1
在第 7 位(第 8 位)中,即使源不是负数,结果也将为负数。同样,如果来源有 0
在第 7 位,结果将为正,即使源为负。
看这个例子:
for _, v := range []int16{4336, -129, 8079} {
fmt.Printf("Source : %v\n", v)
fmt.Printf("Source hex: %4x\n", uint16(v))
fmt.Printf("Result hex: %4x\n", uint8(int8(v)))
fmt.Printf("Result : %4v\n", uint8(int8(v)))
fmt.Println()
}
输出(在 Go Playground 上尝试):
Source : 4336
Source hex: 10f0
Result hex: f0
Result : -16
Source : -129
Source hex: ff7f
Result hex: 7f
Result : 127
Source : 8079
Source hex: 1f8f
Result hex: 8f
Result : -113
查看相关问题:
When casting an int64 to uint64, is the sign retained?
Format printing the 64bit integer -1 as hexadecimal deviates between golang and C
关于Golang 移位运算符转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54998543/