编译 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
我有一些要初始化的浮点数 (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