【问题标题】:Lua - packing IEEE754 single-precision floating-point numbersLua - 打包 IEEE754 单精度浮点数
【发布时间】:2013-01-03 05:09:11
【问题描述】:

我想在纯 Lua 中创建一个函数,生成一个 fraction(23 位)、一个 exponent(8 位)和一个 sign(1位)从一个数字,使该数字约等于math.ldexp(fraction, exponent - 127) * (sign == 1 and -1 or 1),然后将生成的值打包成32位。

数学库中的某个函数引起了我的注意:

freexp函数将浮点值(v)分解为尾数(m)和指数(n),使得m的绝对值大于等于0.5且小于1.0,v = m * 2^n。

注意 math.ldexp 是逆运算。

但是,我想不出任何方法可以正确打包非整数。由于这个函数返回的尾数不是整数,所以不知道能不能用。

有没有任何有效的方法来做类似于math.frexp() 的事情,它返回一个整数作为尾数?或者有没有更好的方法在 Lua 中以 IEEE754 单精度浮点格式打包数字?

提前谢谢你。

编辑

我在此展示我制作的函数的(希望是)最终版本:

function PackIEEE754(number)
    if number == 0 then
        return string.char(0x00, 0x00, 0x00, 0x00)
    elseif number ~= number then
        return string.char(0xFF, 0xFF, 0xFF, 0xFF)
    else
        local sign = 0x00
        if number < 0 then
            sign = 0x80
            number = -number
        end
        local mantissa, exponent = math.frexp(number)
        exponent = exponent + 0x7F
        if exponent <= 0 then
            mantissa = math.ldexp(mantissa, exponent - 1)
            exponent = 0
        elseif exponent > 0 then
            if exponent >= 0xFF then
                return string.char(sign + 0x7F, 0x80, 0x00, 0x00)
            elseif exponent == 1 then
                exponent = 0
            else
                mantissa = mantissa * 2 - 1
                exponent = exponent - 1
            end
        end
        mantissa = math.floor(math.ldexp(mantissa, 23) + 0.5)
        return string.char(
                sign + math.floor(exponent / 2),
                (exponent % 2) * 0x80 + math.floor(mantissa / 0x10000),
                math.floor(mantissa / 0x100) % 0x100,
                mantissa % 0x100)
    end
end
function UnpackIEEE754(packed)
    local b1, b2, b3, b4 = string.byte(packed, 1, 4)
    local exponent = (b1 % 0x80) * 0x02 + math.floor(b2 / 0x80)
    local mantissa = math.ldexp(((b2 % 0x80) * 0x100 + b3) * 0x100 + b4, -23)
    if exponent == 0xFF then
        if mantissa > 0 then
            return 0 / 0
        else
            mantissa = math.huge
            exponent = 0x7F
        end
    elseif exponent > 0 then
        mantissa = mantissa + 1
    else
        exponent = exponent + 1
    end
    if b1 >= 0x80 then
        mantissa = -mantissa
    end
    return math.ldexp(mantissa, exponent - 0x7F)
end

我改进了使用隐式位的方法,并添加了对特殊值(例如 NaN 和无穷大)的适当支持。我的格式基于 catwell 链接到的脚本的格式。

感谢你们两位的宝贵建议。

【问题讨论】:

    标签: floating-point lua ieee-754 pack


    【解决方案1】:

    将从math.frexp() 得到的有效数乘以 2^24,然后从指数中减去 24 以进行补偿。现在有效数字是一个整数。请注意,有效位是 24 位,而不是 23(您需要考虑 IEEE-754 编码中的隐式位)。

    【讨论】:

      【解决方案2】:

      你见过this吗?

      我认为它以稍微简单的方式完成您想要的操作。

      【讨论】:

      • 谢谢,我刚刚查过了。它使用的方法与我的方法非常相似,但使用了更清晰的格式。但是,它存在一些问题(它似乎不支持零或非常小的数字),所以我正在基于它制作自己的版本。完成后我会更新我的帖子。