【问题标题】:Golang shift operator conversionGolang移位运算符转换
【发布时间】:2019-07-26 16:02:57
【问题描述】:

我无法在 golang 中理解 1<<s 如果 var s uint = 33 返回 0。 但是1<<33 返回8589934592。 移位运算符转换如何以 0 的值结束。

我正在阅读语言规范并停留在本节: https://golang.org/ref/spec#Operators

特别是文档中的这一段:

"移位表达式的右操作数必须是无符号整数 type 或者是一个无类型的常量,可以用 uint 类型的值表示。 如果非常量移位表达式的左操作数是无类型的 常量,它首先被隐式转换为它假定的类型 如果移位表达式被其左操作数单独替换。"

来自官方 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

在将int16转换为int8时,数字4336如何变成-16

【问题讨论】:

  • 我在示例中更正了它
  • 在这种情况下,答案是“因为符号位”。我目前无法引用规范,但显然这种转换是通过丢弃较高字节并将其余字节解释为带符号的 8 位 int 来完成的。有符号整数有一个叫做符号位的东西。如果该位为 1,则为负数。
  • 非常有用的信息。它可以帮助我得到它。所以二进制的4336是1000011110000,16是10000。-16是110000?符号保留 2 位,所以 int8 的左边是 6 位,它导致将数字 1000011110000 截断为 10000(即 16)?

标签: go type-conversion operator-keyword


【解决方案1】:

如果你有这个:

var s uint = 33
fmt.Println(1 << s)

然后引用的部分适用:

如果非常量移位表达式的左操作数是无类型常量,则首先将其隐式转换为仅将移位表达式替换为其左操作数时假定的类型。

因为s 不是一个常量(它是一个变量),所以1 &gt;&gt; s 是一个非常量移位表达式。左操作数是1,它是一个无类型常量(例如,int(1) 将是一个类型常量),所以它被转换为如果表达式只是1 而不是1 &lt;&lt; s 将得到的类型:

fmt.Println(1)

在上面,无类型常量1 将被转换为int,因为这是它的默认类型。默认的常量类型在Spec: Constants:

一个无类型的常量有一个默认类型,这是常量在需要类型值的上下文中隐式转换成的类型,例如,在short variable declaration 中,例如i := 0没有显式类型的地方。无类型常量的默认类型分别为boolruneintfloat64complex128string,具体取决于它是布尔型、符文型、整数型、浮点型、复数型, 或字符串常量。

上述结果取决于架构。如果 int 是 32 位,则它将是 0。如果 int 是 64 位,则它将是 8589934592(因为将 1 位移动 33 次会将其移出 32 位 int 数字)。

在 Go 操场上,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 &lt;&lt; 33,则不一样,这不是非常量移位表达式,您的引用适用于:“非常量移位表达式的左操作数”。 1&lt;&lt;33 是一个常量移位表达式,在“常量空间”处求值,结果将转换为不适合 32 位 intint,因此会出现编译时错误。它适用于变量,因为变量可能会溢出。常量不会溢出:

数值常量表示任意精度的精确值,不会溢出。

How does Go perform arithmetic on constants?

更新:

回答您的添加:从int16 转换为int8 只需保留最低8 位。并且整数使用2's complement 格式表示,如果数字为负数,则最高位为1

这在Spec: Conversions:中有详细说明

整数类型之间转换时,如果值为有符号整数,则符号扩展为隐式无限精度;否则为零扩展。 然后将其截断以适应结果类型的大小。 例如,如果是v := uint16(0x10F0),则为uint32(int8(v)) == 0xFFFFFFF0。转换总是产生一个有效值;没有溢出的迹象。

因此,当您将int16 值转换为int8 时,如果源编号在第7 位(第8 位)有1,即使源不是负数,结果也将为负数。同样,如果源在第 7 位有0,即使源为负,结果也会为正。

看这个例子:

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

【讨论】:

  • 感谢您提供详细信息。但是在谷歌游乐场上,如果它输入 fmt.Println(1
  • @Salimd83 如果你写1 &lt;&lt; 33,那是不一样的,那不是一个非常量移位表达式,你的引用适用于:"非-的左操作数常量移位表达式”1&lt;&lt;33 是一个常量移位表达式,在“常量空间”处求值,结果将转换为不适合 32 位 intint。它适用于变量,因为变量可能会溢出。常量不会溢出。
【解决方案2】:

您正在 32 位模式下构建和运行程序(去游乐场?)。其中,int 为 32 位宽,其行为与 int32 相同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-15
    • 2019-02-21
    • 1970-01-01
    相关资源
    最近更新 更多