65.9K
CodeProject 正在变化。 阅读更多。
Home

IEEE 754 转换

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (3投票s)

2019 年 1 月 22 日

CPOL

3分钟阅读

viewsIcon

29767

downloadIcon

540

两种方法可以进行 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 或浮点值,我仍然可以使用这些方法通过协议共享信息。

© . All rights reserved.