【问题标题】:Reasons to use int32 in a protobuf在 protobuf 中使用 int32 的原因
【发布时间】:2018-07-27 00:50:09
【问题描述】:

在 gpb proto2 (https://developers.google.com/protocol-buffers/docs/proto#scalar) 的标量类型的描述中说:

  • int32

    使用可变长度编码。对负编码效率低下 数字 - 如果您的字段可能有负值,请使用 sint32 而是。

  • sint32

    使用可变长度编码。带符号的 int 值。这些更 比常规 int32 更有效地编码负数。

对于正值,sint32 是否与 int32 一样有效?

也就是说,有什么理由使用int32?

如果使用什么语言很重要,我只对 C++ 感兴趣。

【问题讨论】:

  • 你需要变长整数吗?如果没有,固定长度可能会更好。
  • 是的,需要可变长度。

标签: c++ protocol-buffers


【解决方案1】:

https://developers.google.com/protocol-buffers/docs/encoding#signed-integers

有符号变量通过在正负值之间交替进行编码。例如,

   value     int32    zigzag    sint32
          (binary)            (binary)
       0  00000000         0  00000000
      -1  11111111         1  00000001
          11111111
          11111111
          11111111
          00001111
       1  00000001         2  00000010
      -2  11111110         3  00000011
          11111111
          11111111
          11111111
          00001111
...
      63  00111111       126  01111110
     -64  11000000       127  01111111
          11111111
          11111111
          11111111
          00001111
      64  01000000       128  10000000
                              00000001
...

平均而言,将正数编码为sint 比编码为int 需要多一位。

(展开并运行以下 sn-p 以进行现场演示。)

function encode_varint(number) {
  if (!number) return [0];
  var bytes = [];
  while (number) {
    var byte = number & 0x7F;
    number >>>= 7;
    if (number) byte |= 0x80;
    bytes.push(byte);
  }
  return bytes;
}
function format_bytes(bytes) {
  var output = '';
  for (var i = 0; i < bytes.length; i++) {
    if (i) output += ' ';
    output += bytes[i].toString(2).padStart(8, '0');
  }
  return output;
}
var valueElem = document.getElementById('value');
var int32Elem = document.getElementById('int32');
var sint32Elem = document.getElementById('sint32');
function update() {
  var value = parseInt(valueElem.value);
  var int32 = encode_varint(value);
  var sint32 = encode_varint(value << 1 ^ -(value < 0));
  int32Elem.value = format_bytes(int32);
  sint32Elem.value = format_bytes(sint32);
}
valueElem.addEventListener('change', update);
update();
#varint {
  display: grid;
  grid-template-columns: max-content auto;
  grid-row-gap: 1ex;
  grid-column-gap: 1ch;
}
#varint label {
  text-align: right;
}
#varint input {
  font-family: monospace;
}
<form id='varint' onsubmit='return false'>
  <label for='value'>value</label>
  <input id='value' type='number' value='0'>
  <label for='int32'>int32</label>
  <input id='int32' type='text' readonly>
  <label for='sint32'>sint32</label>
  <input id='sint32' type='text' readonly>
</form>

【讨论】:

    【解决方案2】:

    int32 的一些正值会比 sint32 小,但最多只有一个字节。

    基本上,protobuf 对数字使用“base128”编码,其中编码值的每个字节都有 7 位值数据和一位作为“结束”标记,以查找编码值的结尾。 “int32”将数字视为 32 位二进制补码并将其编码为无符号 32 位数字,因此负值将被编码为大的正值并且始终需要 5 个字节。另一方面,“sint32”编码为一种奇怪的符号大小样式编码,符号位被转换为最低有效位(为什么他们不只使用正常的 2s 补码是一个谜——同样紧凑,但更简单解码/编码)。

    结果是单字节 int32 可以表示 0-127 范围内的数字,而单字节 sint32 可以表示 -64..63 范围内的数字。所以值 64..127 将需要 2 个字节作为 sint32,并且只需要一个作为 int32

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-09
      • 1970-01-01
      相关资源
      最近更新 更多