duansanzi5265 2015-08-19 13:17

# 可变长度二进制补码int64

I'm attempting to write a Go program to parse ans.1 BER two's complement integer encoding. However, the integer can have either 1, 2, 3 or 4 byte length encoding (depending on its size).

According to the specification (http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf) the leftmost bit is always the complement.

What's a clean way to do this?

``````func ParseInt(b []byte) (int64, error) {
switch len(b) {
case 1:
// this works
return int64(b[0]&0x7f) - int64(b[0]&0x80), nil
case 2:
// left most byte of b[0] is -32768
case 3:
// left most byte of b[0] is -8388608
case 4:
// left most byte of b[0] is -2147483648 (and so on for 5, 6, 7, 8)
case 5:
case 6:
case 7:
case 8:
default:
return 0, errors.New("value does not fit in a int64")
}
}

ParseInt([]byte{0xfe})       // should return (-2, nil)
ParseInt([]byte{0xfe, 0xff}) // should return (-257, nil)
ParseInt([]byte{0x01, 0x00}) // should return (256, nil)
``````
• 写回答

#### 1条回答默认 最新

• dongye3917 2015-08-19 14:29
关注

Easier to understand if you read the bytes from the end:

• You don't have to shift the last byte
• Left-shift the last byte by 8 (8 bits in a byte)
• Left-shift the 2nd last byte by 16
• ...
• And from the first byte only use 7 bits, the leftmost bit is special.

The leftmost bit of the first byte `b[0]&080` tells if you have to add an offset to the result. The offset to be optionally added is `-1` multiplied by the number your input would mean by having this one bit set and all others being `0`, that is `-1 * (1 << (len(b)*8 - 1)) = 0x80 << (len(b)*8 - 8)`.

Examples. If input is...

• 1 byte:
`int64(b[0]&0x7f) - int64(b[0]&0x80)`
• 2 bytes:
`int64(b[0]&0x7f)<<8 + int64(b[1]) - int64(b[0]&0x80)<<8`
• 3 bytes:
`int64(b[0]&0x7f)<<16 + int64(b[1])<<8 + int64(b[2]) - int64(b[0]&0x80)<<16`

All these cases can be covered with a nice loop.

Here's a compact implementation (try it on the <kbd>Go Playground</kbd>):

``````func ParseInt(b []byte) (int64, error) {
if len(b) > 8 {
return 0, errors.New("value does not fit in a int64")
}

var n int64
for i, v := range b {
shift := uint((len(b) - i - 1) * 8)
if i == 0 && v&0x80 != 0 {
n -= 0x80 << shift
v &= 0x7f
}
n += int64(v) << shift
}
return n, nil
}
``````
本回答被题主选为最佳回答 , 对您是否有帮助呢?
评论

#### 悬赏问题

• ¥15 关于模型训练的一个问题
• ¥15 装了几千台服务器从来没遇到这种问题，哎看下哪位帮我解决吧
• ¥15 单片机程序上的困难问题
• ¥15 请教某软件缓存Ts文件破解合并mp4的方法
• ¥15 求小游戏炸弹人中关于敌人的C＋＋代码
• ¥15 拿到这个服务器最高权限有偿
• ¥50 来个抓app跳转支付宝转链接的
• ¥15 remotes安装提示没有description文件
• ¥15 AttributeError: 'NoneType' object has no attribute 'drop_duplicates'报错
• ¥15 以下代码，运行结果报错