【问题标题】:Parsing IEEE754 Double-Precision floats in Pure Lua?在纯 Lua 中解析 IEEE754 双精度浮点数?
【发布时间】:2012-02-28 09:30:58
【问题描述】:

我有固定大小的双精度数组,以 IEEE754 格式编码,谁能指出任何可以做相关事情的 Lua 代码?

更新:我不能发布这个问题,因为它太短了,所以这是我在解决这个问题的过程中编写的一些代码 - 这会将二进制字符串转换为位字符串,例如 "0011000"

-- get string of bits for given byte
function byte2bits(i)
   local result=""
   for c=1,8 do
      nextByte = i % 2
      i = (i - nextByte)/2
      result = result .. nextByte
   end
   return string.reverse(result)
end

-- get a string of bits from string of bytes
function str2bits(s)
   result=''
   for i = 1, string.len(s) do
      --print(string.byte(s, i))
      result=result .. byte2bits(string.byte(s,i))
   end
   return result
end

【问题讨论】:

  • 你想做什么需要这个? Lua 不是低级语言。这不是你应该在 Lua 中做的事情。如果您尝试解析一些二进制文件,最好使用一些可以从文件中读取整数/浮点数/等的辅助 C 或 C++ 代码。
  • 这是一个只支持 Lua 扩展的基础设施组件。
  • 你能更好地描述你数组中的项目吗? “以 IEEE754 格式编码的双精度数组”不够清楚。那么你有一个字符串数组吗?请包含您要解析的数组的示例。

标签: floating-point lua bit-manipulation ieee-754


【解决方案1】:

幸运的是,8 年后情况发生了变化:

这里有一个仍在更新的纯 lua 库,与其他结构库非常相似:https://luarocks.org/modules/deepakjois/vstruct

在其他格式中,它可以解析任何字节序的浮点数和双精度数。

例子:

local readfloat = vstruct.compile("f4") -- compile a parser, f4 is a 4 byte float
local results = {}
readfloat:read("aaaa",results) -- can return either a new table or reuse one as done here
print(results[1]) -- 2.5984589414244e+20

【讨论】:

    【解决方案2】:

    看看Lua-struct。它小巧灵活,没有依赖关系。

    【讨论】:

    • 他说它适用于不允许二进制模块的主机;他需要纯lua技术或实现。
    【解决方案3】:

    我实际上需要这样做并来这里寻求答案。看起来以前没有人这样做过,所以我最终自己制作了一些东西。我没有对每个案例都进行详尽的测试,但它能够可靠地正确编码和解码数字,输入和输出数字之间没有错误。

    我编写的函数使用二进制字符串,但任何需要它的人都应该能够轻松地将其调整为自己的用途。

    这是我的代码:

    --Define some commonly used constants here so we don't have to do this at runtime
    --ln(2), used for change of base down the line
    local log2 = math.log(2)
    
    --Used to convert the fraction into a (very large) integer
    local pow2to52 = math.pow(2,52)
    
    --Used for bit-shifting
    local f08 = math.pow(2, 8)
    local f16 = math.pow(2,16)
    local f24 = math.pow(2,24)
    local f32 = math.pow(2,32)
    local f40 = math.pow(2,40)
    local f48 = math.pow(2,48)
    
    function encodeDouble(number)
        --IEEE double-precision floating point number
        --Specification: https://en.wikipedia.org/wiki/Double-precision_floating-point_format
    
        --Separate out the sign, exponent and fraction
        local sign      = number < 0 and 1 or 0
        local exponent  = math.ceil(math.log(math.abs(number))/log2) - 1
        local fraction  = math.abs(number)/math.pow(2,exponent) - 1
    
        --Make sure the exponent stays in range - allowed values are -1023 through 1024
        if (exponent < -1023) then 
            --We allow this case for subnormal numbers and just clamp the exponent and re-calculate the fraction
            --without the offset of 1
            exponent = -1023
            fraction = math.abs(number)/math.pow(2,exponent)
        elseif (exponent > 1024) then
            --If the exponent ever goes above this value, something went horribly wrong and we should probably stop
            error("Exponent out of range: " .. exponent)
        end
    
        --Handle special cases
        if (number == 0) then
            --Zero
            exponent = -1023
            fraction = 0
        elseif (math.abs(number) == math.huge) then
            --Infinity
            exponent = 1024
            fraction = 0
        elseif (number ~= number) then
            --NaN
            exponent = 1024
            fraction = (pow2to52-1)/pow2to52
        end
    
        --Prepare the values for encoding
        local expOut = exponent + 1023                                  --The exponent is an 11 bit offset-binary
        local fractionOut = fraction * pow2to52                         --The fraction is 52 bit, so multiplying it by 2^52 will give us an integer
    
    
        --Combine the values into 8 bytes and return the result
        return char(
                128*sign + math.floor(expOut/16),                       --Byte 0: Sign and then shift exponent down by 4 bit
                (expOut%16)*16 + math.floor(fractionOut/f48),           --Byte 1: Shift fraction up by 4 to give most significant bits, and fraction down by 48
                math.floor(fractionOut/f40)%256,                        --Byte 2: Shift fraction down 40 bit
                math.floor(fractionOut/f32)%256,                        --Byte 3: Shift fraction down 32 bit
                math.floor(fractionOut/f24)%256,                        --Byte 4: Shift fraction down 24 bit
                math.floor(fractionOut/f16)%256,                        --Byte 5: Shift fraction down 16 bit
                math.floor(fractionOut/f08)%256,                        --Byte 6: Shift fraction down 8 bit
                math.floor(fractionOut % 256)                           --Byte 7: Last 8 bits of the fraction
            )
    end
    
    function decodeDouble(str)
        --Get bytes from the string
        local byte0 = byte(substr(str,1,1))
        local byte1 = byte(substr(str,2,2))
        local byte2 = byte(substr(str,3,3))
        local byte3 = byte(substr(str,4,4))
        local byte4 = byte(substr(str,5,5))
        local byte5 = byte(substr(str,6,6))
        local byte6 = byte(substr(str,7,7))
        local byte7 = byte(substr(str,8,8))
    
        --Separate out the values
        local sign = byte0 >= 128 and 1 or 0
        local exponent = (byte0%128)*16 + math.floor(byte1/16)
        local fraction = (byte1%16)*f48 
                         + byte2*f40 + byte3*f32 + byte4*f24 
                         + byte5*f16 + byte6*f08 + byte7
    
        --Handle special cases
        if (exponent == 2047) then
            --Infinities
            if (fraction == 0) then return math.pow(-1,sign) * math.huge end
    
            --NaN
            if (fraction == pow2to52-1) then return 0/0 end
        end
    
        --Combine the values and return the result
        if (exponent == 0) then
            --Handle subnormal numbers
            return math.pow(-1,sign) * math.pow(2,exponent-1023) * (fraction/pow2to52)
        else
            --Handle normal numbers
            return math.pow(-1,sign) * math.pow(2,exponent-1023) * (fraction/pow2to52 + 1)
        end
    end
    

    【讨论】:

      猜你喜欢
      • 2013-01-03
      • 2016-06-24
      • 1970-01-01
      • 2018-02-23
      • 2018-07-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多