为什么输出此代码:
package main
import (
"fmt"
"encoding/binary"
)
func main() {
var myByte byte = 18
array := []byte{myByte}
val, n := binary.Varint(array)
fmt.Printf("value: %d, num bytes: %d\n", val, n)
}
value: 9, num bytes: 1
代替value: 18, num bytes: 1
它可能与两个补码有关,但我没有看到如何。
答案 0 :(得分:1)
TLDR:使用Uvarint
方法正确解码无符号字节..默认情况下为byte
。
字节存储无符号(默认情况下,字节无符号 - 在大多数语言中它是uint8
的别名。)
当您解码号码时,您需要拨打binary.Varint
..来解码已签名的号码。由于符号位,这导致数字不正确。
使用binary.Uvarint
..即解码无符号数字,您将得到正确的结果:
val, n := binary.Uvarint(array) // val = 18, n = 1
让我们来看看你的号码 - 18.二进制,就是这样:
00010010
binary.Varint
功能如下:
func Varint(buf []byte) (int64, int) {
ux, n := Uvarint(buf) // ok to continue in presence of error
x := int64(ux >> 1)
if ux&1 != 0 {
x = ^x
}
return x, n
}
基本上,它会首先获得您提供的无符号值:18
。
然后它将所有字节移过1.这导致:
00001001
这是9
的二进制表示。请注意,符号位仍为0 - 表示正数。然后,它检查是否按原样反转结果并使用18
原始值(1
)。它之所以这样做,是因为它使用的是"我知道这个号码已签名"上下文 - 这就是函数存在的原因:
00010010
00000001
--------
00000000
= 0
此时,零确实等于零 - 因此方法返回x
- 即9。
使用1作为输入:
00000001
向右移动:
00000000
AND 原始号码(1) 1:
00000001
00000001
--------
= 1
此时,结果不等于零..所以结果反转:
11111111
这是-1
的签名表示(注意符号位现在为1 ..表示负数)。
答案 1 :(得分:0)
是。检查此修改后的版本:http://play.golang.org/p/AyP2a4gue8
package main
import (
"encoding/binary"
"fmt"
)
func main() {
for i := 0; i < 100; i++ {
var myByte byte = byte(i)
array := []byte{myByte}
val, n := binary.Varint(array)
fmt.Printf("int %d value: %d, num bytes: %d\n", i, val, n)
}
}
产生以下输出:
int 0 value: 0, num bytes: 1
int 1 value: -1, num bytes: 1
int 2 value: 1, num bytes: 1
int 3 value: -2, num bytes: 1
int 4 value: 2, num bytes: 1
int 5 value: -3, num bytes: 1
int 6 value: 3, num bytes: 1
int 7 value: -4, num bytes: 1
int 8 value: 4, num bytes: 1
int 9 value: -5, num bytes: 1
int 10 value: 5, num bytes: 1
int 11 value: -6, num bytes: 1
int 12 value: 6, num bytes: 1
int 13 value: -7, num bytes: 1
int 14 value: 7, num bytes: 1
int 15 value: -8, num bytes: 1
int 16 value: 8, num bytes: 1
int 17 value: -9, num bytes: 1
int 18 value: 9, num bytes: 1
你可以看到否定与正面之间的“锯齿形”。这是因为,根据documented binary format,varints使用“zig-zag”编码,因此小绝对值的值用较小的值编码。