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
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 在若依框架下实现人脸识别
  • ¥15 网络科学导论,网络控制
  • ¥100 安卓tv程序连接SQLSERVER2008问题
  • ¥15 利用Sentinel-2和Landsat8做一个水库的长时序NDVI的对比,为什么Snetinel-2计算的结果最小值特别小,而Lansat8就很平均
  • ¥15 metadata提取的PDF元数据,如何转换为一个Excel
  • ¥15 关于arduino编程toCharArray()函数的使用
  • ¥100 vc++混合CEF采用CLR方式编译报错
  • ¥15 coze 的插件输入飞书多维表格 app_token 后一直显示错误,如何解决?
  • ¥15 vite+vue3+plyr播放本地public文件夹下视频无法加载
  • ¥15 c#逐行读取txt文本,但是每一行里面数据之间空格数量不同