doumenshi1475 2019-03-05 08:39
浏览 102
已采纳

Golang移位运算符转换

I can not understand in golang how 1<<s return 0 if var s uint = 33. But 1<<33 return 8589934592. How a shift operator conversion end up with a value of 0.

I'm reading the language specification and stuck in this section: https://golang.org/ref/spec#Operators

Specifically this paragraph from docs:

"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."

Some example from official Golang docs:

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

Update:

Another very related question, with an example:

package main

import (
    "fmt"
)

func main() {
v := int16(4336)
    fmt.Println(int8(v))
}

This program return -16

How does the number 4336 become -16 in converting int16 to int8

  • 写回答

2条回答 默认 最新

  • douzhi4056 2019-03-05 08:47
    关注

    If you have this:

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

    Then the quoted part applies:

    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.

    Because s is not a constant (it's a variable), therefore 1 >> s is a non-constant shift expression. And the left operand is 1 which is an untyped constant (e.g. int(1) would be a typed constant), so it is converted to a type that it would get if the expression would be simply 1 instead of 1 << s:

    fmt.Println(1)
    

    In the above, the untyped constant 1 would be converted to int, because that is its default type. Default type of constants is in 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.

    And the result of the above is architecture dependent. If int is 32 bits, it will be 0. If int is 64 bits, it will be 8589934592 (because shifting a 1 bit 33 times will shift it out of a 32-bit int number).

    On the Go playground, size of int is 32 bits (4 bytes). See this example:

    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)
    

    The above outputs (try it on the Go Playground):

    int size: 4
    0
    0
    8589934592
    

    If I run the above app on my 64-bit computer, the output is:

    int size: 8
    8589934592
    0
    8589934592
    

    Also see The Go Blog: Constants for how constants work in Go.

    Note that if you write 1 << 33, that is not the same, that is not a non-constant shift expression, which your quote applies to: "the left operand of a non-constant shift expression". 1<<33 is a constant shift expression, which is evaluated at "constant space", and the result would be converted to int which does not fit into a 32-bit int, hence the compile-time error. It works with variables, because variables can overflow. Constants do not overflow:

    Numeric constants represent exact values of arbitrary precision and do not overflow.

    See How does Go perform arithmetic on constants?

    Update:

    Answering your addition: converting from int16 to int8 simply keeps the lowest 8 bits. And integers are represented using the 2's complement format, where the highest bit is 1 if the number is negative.

    This is detailed in 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.

    So when you convert a int16 value to int8, if source number has a 1 in bit position 7 (8th bit), the result will be negative, even if the source wasn't negative. Similarly, if the source has 0 at bit position 7, the result will be positive, even if the source is negative.

    See this example:

    for _, v := range []int16{4336, -129, 8079} {
        fmt.Printf("Source    : %v
    ", v)
        fmt.Printf("Source hex: %4x
    ", uint16(v))
        fmt.Printf("Result hex: %4x
    ", uint8(int8(v)))
        fmt.Printf("Result    : %4v
    ", uint8(int8(v)))
        fmt.Println()
    }
    

    Output (try it on the 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
    

    See related questions:

    When casting an int64 to uint64, is the sign retained?

    Format printing the 64bit integer -1 as hexadecimal deviates between golang and C

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 matlab生成电测深三层曲线模型代码
  • ¥50 随机森林与房贷信用风险模型
  • ¥50 buildozer打包kivy app失败
  • ¥30 在vs2022里运行python代码
  • ¥15 不同尺寸货物如何寻找合适的包装箱型谱
  • ¥15 求解 yolo算法问题
  • ¥15 虚拟机打包apk出现错误
  • ¥15 用visual studi code完成html页面
  • ¥15 聚类分析或者python进行数据分析
  • ¥15 三菱伺服电机按启动按钮有使能但不动作