Lua - 打包 IEEE754 单精度浮点数 [英] Lua - packing IEEE754 single-precision floating-point numbers
问题描述
我想在纯 Lua 中创建一个函数,生成一个分数(23 位)、一个指数(8 位)和一个符号(1 位)来自一个数,使该数约等于 math.ldexp(fraction, exponent - 127) * (sign == 1 and -1 or 1)
,然后将生成的值打包成 32 位.
I want to make a function in pure Lua that generates a fraction (23 bits), an exponent (8 bits), and a sign (1 bit) from a number, so that the number is approximately equal to math.ldexp(fraction, exponent - 127) * (sign == 1 and -1 or 1)
, and then packs the generated values into 32 bits.
数学库中的某个函数引起了我的注意:
A certain function in the math library caught my attention:
frexp 函数将浮点值 (v) 分解为尾数 (m) 和指数 (n),使得 m 的绝对值大于等于 0.5 且小于 1.0,并且 v= m * 2^n.
The frexp function breaks down the floating-point value (v) into a mantissa (m) and an exponent (n), such that the absolute value of m is greater than or equal to 0.5 and less than 1.0, and v = m * 2^n.
注意 math.ldexp 是逆运算.
Note that math.ldexp is the inverse operation.
但是,我想不出任何方法来正确打包非整数.由于这个函数返回的尾数不是整数,不知道能不能用.
However, I can't think of any way to pack non-integer numbers properly. As the the mantissa returned by this function is not an integer, I'm not sure if I can use it.
是否有任何有效的方法可以执行类似于返回整数作为尾数的 math.frexp()
的操作?或者是否有更好的方法在 Lua 中以 IEEE754 单精度浮点格式打包数字?
Is there any efficient way to do something similar to math.frexp()
which returns an integer as the mantissa? Or is there perhaps a better way to pack numbers in the IEEE754 single-precision floating-point format in Lua?
提前致谢.
编辑
我在此展示我制作的函数的(希望如此)最终版本:
I hereby present the (hopefully) final version of the functions I made:
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 的格式.
I improved the way to utilise the implicit bit and added proper support for special values such as NaN and infinity. I based the formatting on that of the script catwell linked to.
感谢你们两位的宝贵建议.
I thank both of you for your great advice.
推荐答案
将从 math.frexp()
得到的有效数乘以 2^24,并从指数中减去 24 以进行补偿.现在有效数是一个整数.请注意,有效数是 24 位,而不是 23(您需要考虑 IEEE-754 编码中的隐式位).
Multiply the significand you get from math.frexp()
by 2^24, and subtract 24 from the exponent to compensate. Now the significand is an integer. Note that the significand is 24 bits, not 23 (you need to account for the implicit bit in the IEEE-754 encoding).
这篇关于Lua - 打包 IEEE754 单精度浮点数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!