如何使用Lua将一对字节转换为带符号的16位整数?

我正在编写一个需要将小的(最多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位:

-- 正数没问题
print( read_i16(dump_i16(17)) ) -- 17
print( read_i16(dump_i16(42)) ) -- 42

-- 负数返回不正确
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代码。

点赞
用户868247
用户868247

你需要使用逻辑右移来处理符号。参见>>>和>>之间的区别。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
2016-12-03 03:24:18
用户1847592
用户1847592

Lua 5.3 有特殊的转换函数。

要将一个整数-32768..32767 转换为长度为2的字符串,采用大端序:

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

要将其转换回来(将“a_string”的前两个字节转换):

local an_integer = string.unpack(">i2", a_string)
2016-12-03 07:50:58
用户90511
用户90511

一种修复read_i16的方法是使用内部由string.pack使用的符号扩展算法:

function read_i16(b1, b2)
  assert (0 <= b1 and b1 <= 0xff)
  assert (0 <= b2 and b2 <= 0xff)
  local mask = (1 << 15)
  local res  = (b1 << 8) | (b2 << 0)
  return (res ~ mask) - mask
end
2016-12-03 13:31:29