IEEE 754 转换





4.00/5 (3投票s)
两种方法可以进行 IEEE 754 转换(32 位)打包和解包
引言
本文将展示如何根据 IEEE 754 规则将 float
值转换为整数。
我将展示两种方法。
其中一种比另一种更快,特别是在 unpack
函数上。
背景
有时,您需要通过协议(串行或网络)发送一个 float
值,但您使用的协议不支持发送/接收 float
值;这意味着该协议仅支持整数(有符号或无符号)。这在开发微控制器项目期间非常常见。
根据我的个人经验,我使用 Modbus 规则在两个不同的微控制器系列(STM32F3 和 STM32F7)之间建立了串行通信。
如果发生这种情况,您需要找到一种方法将 float
转换为整数(从发送方的角度来看),然后将整数转换为其 float
值(从接收方的角度来看)。
一个非常常见的方法是使用 IEEE 754 转换。
代码基于 32 位,但可以轻松扩展到 64 位。如果您需要更高的精度,这可能是必要的。
在网络上,有很多关于这种方法的文献,例如,https://en.wikipedia.org/wiki/IEEE_754。
Using the Code
此代码具有很强的可移植性,因为它使用非常标准的 C 代码编写(我正在微控制器、Windows O.S、Linux 和嵌入式世界中使用它)。
我创建了两个文件(头文件和代码文件)和两个函数。
以下是它们的原型和解释。
此函数返回一个无符号的 long
值,该值是输入 float
值的表示形式。
我将其命名为 pack
。
uint32_t pack754_32 ( float f );
unpaack
函数返回 float
值,并将其与无符号的 long
值一起传递给它。
float unpack754_32( uint32_t floatingToIntValue );
如果输入值没有有效的 IEEE 754 表示形式,则返回一个未定义的值。我将其命名为 unpack
。
在这个例子中,为了实现平台可移植性,我使用了定义类型 uint32_t
(代表无符号的 long
).
// float unpack754_32 ( uint32_t floatingToIntValue );
FIRST WAY (faster) // Those methods use the implicit conversion of the C language
uint32_t pack754_32( float f )
{
uint32_t *pfloatingToIntValue;
pfloatingToIntValue = &f;
return (*pfloatingToIntValue);
}
float unpack754_32( uint32_t floatingToIntValue )
{
float *pf, f;
pf = &(floatingToIntValue);
f = *pf;
return f;
}
第二种方法(更学术性)
通过使用联合及其隐式转换,这是另一种将 float
转换为无符号 long
值,反之亦然的方法。此方法将使用位运算功能。联合将把值 f
封装/解封装成其表示形式
typedef union UnFloatingPointIEEE754
{
struct
{
unsigned int mantissa : 23;
unsigned int exponent : 8;
unsigned int sign : 1;
} raw;
float f;
} UFloatingPointIEEE754;
Bit
运算提取位,以便根据 IEEE 754 方法创建所需的值(指数和尾数)。
我使用了 #define
而不是函数或内联函数,因为这是一种更快速的方法。
#define NTH_BIT(b, n) ((b >> n) & 0x1)
#define BYTE_TO_BIN(b) (( b & 0x80 ) ) |\
(( b & 0x40 ) ) |\
(( b & 0x20 ) ) |\
(( b & 0x10 ) ) |\
(( b & 0x08 ) ) |\
(( b & 0x04 ) ) |\
(( b & 0x02 ) ) |\
( b & 0x01 )
#define MANTISSA_TO_BIN(b) (( b & 0x400000 ) ) |\
(( b & 0x200000 ) ) |\
(( b & 0x100000 ) ) |\
(( b & 0x80000 ) ) |\
(( b & 0x40000 ) ) |\
(( b & 0x20000 ) ) |\
(( b & 0x10000 ) ) |\
(( b & 0x8000 ) ) |\
(( b & 0x4000 ) ) |\
(( b & 0x2000 ) ) |\
(( b & 0x1000 ) ) |\
(( b & 0x800 ) ) |\
(( b & 0x400 ) ) |\
(( b & 0x200 ) ) |\
(( b & 0x100 ) ) |\
(( b & 0x80 ) ) |\
(( b & 0x40 ) ) |\
(( b & 0x20 ) ) |\
(( b & 0x10 ) ) |\
(( b & 0x08 ) ) |\
(( b & 0x04 ) ) |\
(( b & 0x02 ) ) |\
( b & 0x01 )
最后,这是 pack
/unpack
函数的定义。
这些函数使用之前的定义来返回所需的值。
pack
函数使用联合的隐式转换。通过分配联合的值,符号、指数和尾数将自动拟合。
uint32_t pack754_32 ( float f )
{
UFloatingPointIEEE754 ieee754;
uint32_t floatingToIntValue = 0;
ieee754.f = f;
floatingToIntValue = (((NTH_BIT(ieee754.raw.sign, 0) << 8) |
(BYTE_TO_BIN(ieee754.raw.exponent))) << 23 ) | MANTISSA_TO_BIN(ieee754.raw.mantissa);
return floatingToIntValue;
}
unpack
函数将使用位运算来创建根据 IEEE754 标准创建 ad-hoc 无符号 int
值。
float unpack754_32( uint32_t floatingToIntValue )
{
UFloatingPointIEEE754 ieee754; unsigned int mantissa = 0;
unsigned int exponent = 0 ;
unsigned int sign = 0;
sign = NTH_BIT(floatingToIntValue, 31);
for( int ix=0; ix<8; ix++)
exponent = (exponent | (NTH_BIT(floatingToIntValue, (30-ix))))<<1;
exponent = exponent>>1;
for( int ix=0; ix<23; ix++)
mantissa = (mantissa | (NTH_BIT(floatingToIntValue, (22-ix))))<<1;
mantissa = mantissa >> 1;
ieee754.raw.sign = sign;
ieee754.raw.exponent = exponent;
ieee754.raw.mantissa = mantissa;
return ieee754.f;
}
如何使用它
我还提供了一个非常简单的测试函数,用于封装和解封装一些值。
void TestPackUnpack ( void )
{
uint32_t n;
float f;
n = 0x3FB4FDF4; f= 1.414
f = unpack754_32(n);
n = pack754_32(1.414);
f = unpack754_32(n);
n = pack754_32(-1.259921);
f = unpack754_32(n);
n = pack754_32(0.58);
f = unpack754_32(n);
n = pack754_32(-0.588);
f = unpack754_32(n);
n = pack754_32(2);
f = unpack754_32(n);
n = pack754_32(-3);
f = unpack754_32(n);
}
关注点
我认为本文将重点介绍与 C 语言相关的一些重要功能,例如隐式转换、位运算符等。
IEEE 754 转换方法也可以用来转换整数。通过这种方式,如果我的协议不支持 float
或浮点值,我仍然可以使用这些方法通过协议共享信息。