英文:
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 (
"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)
}
}
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论