2016-12-03 1 views
1

Я пишу программу, которая должна сериализовать небольшие (до 16 бит) целые числа со знаком в двоичный файл. Как часть этого, мне нужно написать функцию, которая может разделить целое на два байта и другую функцию, которая может преобразовать эту пару байтов обратно в исходное целое число.Как преобразовать пару байтов в 16-битное целое число с использованием Lua?

Первая мысль, которая пришла мне на ум, чтобы решить эту проблему так же, как я бы решить это C:

function dump_i16(n) 
    assert (-0x8000 <= n and n < 0x8000) -- 16 bit 
    local b1 = (n >> 8) & 0xff 
    local b2 = (n >> 0) & 0xff 
    return b1, b2 
end 

function read_i16(b1, b2) 
    assert (0 <= b1 and b1 <= 0xff) -- 8 bit 
    assert (0 <= b2 and b2 <= 0xff) -- 8 bit 
    return (b1 << 8) | (b2 << 0) 
end 

Однако эти функции разбить на отрицательные числа, так как Lua числа на самом деле имеют 64 бита и I я только сохранение нижних 16 бит:

-- Positive numbers are OK 
print(read_i16(dump_i16(17))) -- 17 
print(read_i16(dump_i16(42))) -- 42 

-- Negative numbers don't round trip. 
print(read_i16(dump_i16(-1))) -- 65535 = 2^16 - 1 
print(read_i16(dump_i16(-20))) -- 65516 = 2^16 - 20 

что бы быть чистым способом изменить свою read_i16 функции, так что работает правильно для отрицательных чисел?

Я бы предпочел сделать это, используя, по возможности, чистый Lua 5.3, не дойдя до написания кода C.

+0

Ваш dump_i16 производит неподписанные значения [0,256], поэтому он всегда будет терпеть неудачу в ваших утверждениях. – Moop

+0

Вы сказали, что 'read_i16 (dump_i16 (-20) == 65516' и' 65516 == 2^16 -20'. Почему бы вам просто не вычесть «2^16» из результата ...? – siffiejoe

+0

@Moop: Вы правы. Я должен был протестировать, прежде чем редактировать тех, кто находится посреди ночи ... – hugomg

ответ

2

Lua 5.3 имеет специальные функции преобразования.

Чтобы преобразовать целое число -32768..32767 в строку длиной 2 в большом обратном порядке байт заказа:

local a_string = string.pack(">i2", your_integer) 

Для того, чтобы преобразовать его обратно (первые два байта «a_string» преобразовывается):

local an_integer = string.unpack(">i2", a_string) 
+0

Спасибо! Я соглашусь с этим ответом, потому что, хотя на самом деле он не решает мою специфическую проблему, он скорее всего будет полезен будущим читателям. Исходный код для string.pack также имел трюк, который я искал :) – hugomg

1

Вам нужна логическая сдвиг вправо для работы со знаком. См. Difference between >>> and >>. У Lua этого нет, поэтому вы сами делаете. Поскольку по умолчанию число Lua равно 64, вы замаскируете 64 минус количество смен, которое вы делаете.

function dump_i16(n) 
    assert (-0x8000 <= n and n < 0x8000) 
    local b1 = (n >> 8) & ~(-1<<(64-8)) 
    local b2 = (n >> 0) & 0xff 
    return b1, b2 
end 
+0

Есть ли способ изменить 'read_i16' вместо' dump_i16'? Мне нужно, чтобы b1 и b2 совпадали с одним байтом. – hugomg

0

Один из способов исправить read_i16 является использование алгоритма расширения знак, который используется внутри string.pack:

function read_i16(b1, b2) 
    assert (0 <= b1 and b1 <= 0xff) 
    assert (0 <= b1 and b2 <= 0xff) 
    local mask = (1 << 15) 
    local res = (b1 << 8) | (b2 << 0) 
    return (res ~ mask) - mask 
end 
Смежные вопросы