duansanzi5265 2015-08-19 13:17
浏览 37
已采纳

可变长度二进制补码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
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 信号傅里叶变换在matlab上遇到的小问题请求帮助
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作
  • ¥15 求NPF226060磁芯的详细资料