编译 time/macro 在 c99 中交换 float 的字节顺序

Compile time/macro swap of endianess of float in c99

我有一些要初始化的浮点数 (IEEE-754)。浮点数由另一个设备(自动)获取,该设备 运行s big endian 我正在使用 little endian 并且我无法更改它。

通常我会用一些内置函数交换,但它们都是 运行 时间函数。我宁愿不必 init() 函数只是为了交换字节顺序,如果我也可以将它用于 const 初始化,那就太好了。

如果能做到这一点就完美了:

#define COMPILE_TIME_SWAPPED_FLOAT(x) (...) const float a = COMPILE_TIME_SWAPPED_FLOAT(1.0f);

有什么好主意吗?

我想说让预处理器交换一些非字节变量的字节是不可能的。

预处理器不知道数据类型及其在字节级的表示。

如果可以内联字节序反转代码,那么任何半正经的优化编译器都会在编译时计算出反转值。

从中提取反转代码:

inline float ReverseFloat( const float inFloat )
{
   float retVal;
   char *floatToConvert = ( char* ) & inFloat;
   char *returnFloat = ( char* ) & retVal;

   // swap the bytes into a temporary buffer
   returnFloat[0] = floatToConvert[3];
   returnFloat[1] = floatToConvert[2];
   returnFloat[2] = floatToConvert[1];
   returnFloat[3] = floatToConvert[0];

   return retVal;
}

并且在编译器可以看到所有细节的地方使用它:

float reversed10(){
  const float reversed = ReverseFloat(10.0f);
  return reversed;
}

编译为:

reversed10():
        vmovss  xmm0, DWORD PTR .LC0[rip]
        ret
.LC0:
        .long   8257

使用启用了 -O2 的 GCC 7.1。

您可以在这里尝试其他编译器: https://godbolt.org/g/rFmJGP

Compile time/macro swap of endian-ness of float in c99

OP 在使用 "reverse" float 作为 float

时存在其他问题

编码 "reverse" 字节序浮点值的 float 类型的局部变量当然不能初始化为 float。许多反向字节顺序的值将对应于本地 float 中的非数字 (NAN)。分配可能不稳定(保留位模式)。可能是:

// not a good plan
float f1 = some_particulate_not_a_number_bit_pattern;
float f2 = some_other_particulate_not_a_number_bit_pattern;

相反,本地 "reversed" 字节序 float 应该只是 uint32_t、4 字节 structure/union 或以某种方式用 [= 初始化的 4 字节数组15=].

// Demo of why a reversed `float` can fail
// The Not-a-numbers bit to control signaling NaN vs. quiet NaN isn't certainly preserved. 

int main(void) {
  for (;;) {
    union {
      int32_t i32;
      int32_t u32;
      float f;
    } x,y;
    x.i32 = rand();
    y.f = x.f;
    if (x.u32 ^ y.u32) {
      // If bit pattern preserved, this should never print
      //                                 v-------+---- biased exponent max (NaN)
      //                                 |-------|v--- signaling/quiet bit
      // On my machine output is always x1111111 1?xxxxxx xxxxxxxx xxxxxxxx
      printf("%08x\n",   (unsigned) x.u32);
      printf("%08x\n\n", (unsigned) y.u32);
    }
  }
}

输出

7f8181b1
7fc181b1
...

下面使用 复合文字 来满足 OP 的目标。首先用所需的 float 初始化联合的 float 成员。然后从其 uint8_t 成员(根据所需的字节序)逐字节提取它以初始化新的 复合文字的 uint8_t 数组成员。然后提取uint32_t。适用于局部变量。

#include <stdint.h>
#include <stdio.h>

typedef uint32_t float_reversed;

typedef union  {
  uint8_t u8[4];
  float_reversed u32;
  float f;
} endian_f;

#define ENDIAN_FN(_f,_n)  ( (endian_f){.f=(_f)}.u8[(_n)] )
#define ENDIAN_F(_f) ((endian_f){ \
    ENDIAN_FN(_f,3), ENDIAN_FN(_f,2), \
    ENDIAN_FN(_f,1), ENDIAN_FN(_f,0)}.u32)

void print_hexf(void *f) {
  for (size_t i=0; i<sizeof f; i++) {
    printf("%02X", ((unsigned char *)f)[i]);
  }
  puts("");
}

int main(void) {
  float f1 = 1.0f;
  print_hexf(&f1);

  float_reversed f1r = ENDIAN_F(f1);
  print_hexf(&f1r);
  float_reversed f2r = ENDIAN_F(1.0);
  print_hexf(&f2r);
}

输出

0000803F
3F800000
3F800000