В описании скалярных типов для gpb proto2 (https://developers.google.com/protocol-buffers/docs/proto#scalar) это говорит:
int32
Использует кодирование переменной длины. Неэффективно для кодирования отрицательных
числа — если ваше поле может иметь отрицательные значения, используйте sint32
вместо.sint32
Использует кодирование переменной длины. Значение со знаком. Эти более
эффективно кодировать отрицательные числа, чем обычные int32.
Будет ли sint32 одинаково эффективным для положительных значений, как int32?
Другими словами, есть ли причина использовать int32?
Если имеет значение, какой язык используется, меня интересует только C ++.
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
,
(Разверните и запустите следующий фрагмент для живой демонстрации.)
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>
Некоторые положительные значения будут меньше как int32, чем как sint32, но не более чем на один байт.
По сути, protobuf использует кодировку «base128» для чисел, где каждый байт кодированного значения имеет 7 битов данных значения и один бит в качестве маркера «end» для нахождения конца значения кодирования. «int32» обрабатывает число как 32-битное дополнение до двух и кодирует его как 32-разрядное число без знака, поэтому отрицательные значения будут кодироваться как большие положительные значения и всегда требуют 5 байтов. «sint32», с другой стороны, кодирует как странное кодирование в стиле знака-амплитуды с битом знака, перенесенным в младший значащий бит (почему они не просто использовали нормальное дополнение 2s, является загадкой — было бы столь же компактно, но проще декодировать / кодировать).
В результате один байт int32 может представлять числа в диапазоне 0-127, в то время как один байт sint32 представляет числа в диапазоне -64..63. Таким образом, значение 64..127 потребует 2 байта как sint32 и только один как int32