Golang 移位运算符转换

标签 go type-conversion operator-keyword

我无法理解 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在转换int16int8

最佳答案

如果你有这个:

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 is bool, rune, int, float64, complex128 or string 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), then uint32(int8(v)) == 0xFFFFFFF0. The conversion always yields a valid value; there is no indication of overflow.

所以当你转换一个 int16int8 , 如果源编号有 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/

相关文章:

go - 如何在 Go 中解析 ISO 8601 时间戳?

c# - 生成十进制类型的往返字符串

c++ - 重载 + 运算符以添加分数

c++ - 抽象类 C++ 中的增量运算符重载

go - 无法按键获取 gorilla session 值

java - 如何在 Java 中创建命令行应用程序?

go - 无法创建在线网页

string - 在 Julia 中将整数转换为字符串

ios - NSString 的值是 iOS 中的 NSInteger 值吗

c++ - 重载 C++ == 运算符