2016-12-03 787 views
1

我正在写一个程序,需要序列化一个二进制文件的小(最多16位)有符号整数。作为其中的一部分,我需要编写一个可以将整数分成两个字节的函数和另一个可以将这对字节转换回原始整数的函数。如何使用Lua将一对字节转换为带符号的16位整数?

是来到我的脑海里的第一个想法是类似的解决这个问题我将如何解决它为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位,我我只保留低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

@ Mooop:你说得对。我应该在半夜编辑它们之前进行测试... – 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 
相关问题