Go Varint 返回了预期值的一半。

huangapple go评论70阅读模式
英文:

Go Varint Returns Half of Expected Value

问题

这段代码的输出为value: 9, num bytes: 1,而不是value: 18, num bytes: 1。这可能与二进制补码有关,但我没有看出具体原因。

英文:

Why is the output of this code:

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 instead of value: 18, num bytes: 1

It probably has something to do with two's complement, but I'm not seeing how.

答案1

得分: 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) // 在出现错误的情况下继续执行
	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

以1作为输入:

00000001

向右移动:

00000000

将_原始数字(1)_与1进行按位与运算:

00000001
00000001
--------
     = 1

此时,结果不等于零,因此结果被反转:

11111111

这是-1的有符号表示(注意符号位现在为1,表示一个负数)。

英文:

TLDR: Use the Uvarint method to properly decode an unsigned byte .. which is what a byte is by default.

The byte is stored unsigned (as a byte is unsigned by default - it is an alias for uint8 .. in most languages).

When you're decoding the number, you're calling binary.Varint .. which decodes a signed number. This results in the number being incorrect because of the sign bit.

Using binary.Uvarint .. that is, decode an unsigned number, you get the correct result:

val, n := binary.Uvarint(array) // val = 18, n = 1

Expanded example:

Lets take a look at your number - 18. In binary, it is this:

00010010

The binary.Varint function is below:

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
}

Basically, it will first go and get the unsigned value of what you've provided: 18.

It will then shift all of the bytes across by 1. This results in:

00001001

That is the binary representation of 9. Notice the sign bit is still 0 - which means a positive number. It then checks whether to invert the result by bitwise AND'ing the original value (18) with 1. It does this because, it is operating with a "I know this number is signed" context - that's why the function exists:

00010010
00000001
--------
00000000
     = 0

At that point, zero does equal zero - so the method returns x - which is 9.

Lets try with 1

Using 1 as the input:

00000001

Shift it right:

00000000

AND the original number (1) with 1:

00000001
00000001
--------
     = 1

At this point, the result doesn't equal zero.. so the result is inverted:

11111111

This is the signed representation of -1 (notice the sign bit is now 1 .. indicating a negative number).

答案2

得分: 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

你可以看到在负数和正数之间进行了"zig-zag"。这是因为根据文档中的二进制格式,varints使用"zig-zag"编码,以便将小绝对值的值编码为小值。

英文:

Yes. Check this modified version: http://play.golang.org/p/AyP2a4gue8

package main

import (
	&quot;encoding/binary&quot;
	&quot;fmt&quot;
)

func main() {
	for i := 0; i &lt; 100; i++ {
		var myByte byte = byte(i)
		array := []byte{myByte}
		val, n := binary.Varint(array)
		fmt.Printf(&quot;int %d value: %d, num bytes: %d\n&quot;, i, val, n)

	}    
}

which produces the following output:

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

You can see "zig-zagging" between the negatives and positives. This is because, according to the documented binary format, varints use "zig-zag" encoding so that values of small absolute value are encoded with small values.

huangapple
  • 本文由 发表于 2015年1月20日 09:55:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/28036560.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定