2

I have a system where I have to implement to second part. The issue is that I received a float where the bytes are mixed. In the example below, the input is 1E9 (f_orig) and what is receive is this 2.034699e+26 (f_recv). I did the function ABCD_to_CDAB_float but I find it ugly. Is there a better way to to write ABCD_to_CDAB_float(without two temporary variables will already be nice) ?

#include <stdio.h> #include <float.h> #include <string.h> #include <stdint.h> /* Change float byte order */ float ABCD_to_CDAB_float(float infloat) { float outfloat; uint8_t tmp[4]; memcpy(&tmp, &infloat, 4); uint8_t tmp2[] = {tmp[2], tmp[3], tmp[0], tmp[1]}; memcpy(&outfloat, &tmp2, 4); return outfloat; } int main() { float f_orig = 1E9; // This is the value sent printf("f_orig\t%e\n", f_orig); char str_orig[4]; memcpy(&str_orig, &f_orig, 4); printf("str_orig\t%x %x %x %x\n", str_orig[0], str_orig[1], str_orig[2], str_orig[3]); float f_built; // same as f_orig char str_built[] = {0x28, 0x6B, 0x6E, 0x4E}; memcpy(&f_built, &str_built, 4); printf("f_built\t%e\n", f_built); // it prints "1E9" float f_recv; // received float char str_recv[] = {0x6E, 0x4E, 0x28, 0x6B}; memcpy(&f_recv, &str_recv, 4); printf("f_recv\t%e\n", f_recv); // converesion was wrong somewhere char str6[] = {str_recv[2], str_recv[3], str_recv[0], str_recv[1]}; float f_res; memcpy(&f_res, &str6, 4); printf("f_res\t%e\n", f_res); // result is fine printf("%e\n",ABCD_to_CDAB_float(f_recv)); // result is right return 0; } 
2
  • 1
    What is the goal to not use variable, you can do some stuff with XOR but we need to know what you are looking for exactly. Commented Jan 15, 2021 at 13:35
  • You can avoid the memcpy by casting the address of the float to char *. Converting float values from big endian to little endian Commented Jan 15, 2021 at 13:36

4 Answers 4

3

Firstly, please note that removing variables does not automatically imply that it will use less memory. It's quite likely that the compiler will remove unnecessary variables, but it might also add variables if it wants to.

If you absolutely want to get rid of them, you can do this:

float ABCD_to_CDAB_float(float infloat) { float outfloat; char *tmp1 = (char*) &infloat; char *tmp2 = (char*) &outfloat; tmp2[0] = tmp1[2]; tmp2[1] = tmp1[3]; tmp2[2] = tmp1[0]; tmp2[3] = tmp1[1]; return outfloat; } 

But to be honest, I don't see the point. Also, I'd advice against things like this, because it's very easy to get it wrong and cause very hard traced bugs. I guess you also could do something like this: (No, you cannot. See edit below.)

float ABCD_to_CDAB_float(float infloat) { char *tmp1 = (char*) &infloat; char tmp2[] = {tmp[2], tmp[3], tmp[0], tmp[1]}; return *(float*) &tmp2; // This is not ok! It's violating the // strict aliasing rule } 

But again. Avoid these magic tricks if you don't really need them. In some cases, it might impact performance a little bit, but it does not make the code easier to read.

And to be perfectly honest, I'm not 100% sure that this does not violate some of those really strange rules in C. It is a possibility that this leads to undefined behavior. So don't trust me completely on this one. When dealing with pointer casting like this, it's easy to violate the strict aliasing rule

EDIT:

The second example DOES violate the strict aliasing rule, which proves my point that his might be tricky. Thanks to Andrew Henle for pointing it out.

If you want to have absolutely no extra variables, not even pointers, take a look at Eric Postpischil's answer but don't use stuff like that if anyone else is supposed to read it.

Sign up to request clarification or add additional context in comments.

3 Comments

I find your version already cleaner ! Thank you.
@Jona TBH, I prefer your version ;)
And to be perfectly honest, I'm not 100% sure that this does not violate some of those really strange rules in C. return *(float*) &tmp2; is a strict-aliasing violation, and also a potential alignment violation. Your first code example is probably every bit as fast, is really easy to read, and IMO strictly-conforming.
1

To do it with no extra variables:

float ABCD_to_CDAB_float(float x) { return (union { uint8_t u[4]; float f; }) {{ ((uint8_t *) &x)[2], ((uint8_t *) &x)[3], ((uint8_t *) &x)[0], ((uint8_t *) &x)[1] }} .f; } 

To remove some distracting parentheses:

 return (union { uint8_t u[4]; float f; }) {{ 2[(uint8_t *) &x], 3[(uint8_t *) &x], 0[(uint8_t *) &x], 1[(uint8_t *) &x] }} .f; 

1 Comment

Well, you answered the question, but UGH!
1

If you use gcc family compiler I would:

#define SWAP(a,b,type) do{type c = a; (a) = b; (b) = c;} while(0) float ABCD_to_CDAB_float(float x) { union { float f; uint16_t u16[2]; }z = {.f = x}; z.u16[0] = __builtin_bswap16(z.u16[0]); z.u16[1] = __builtin_bswap16(z.u16[1]); SWAP(z.u16[0], z.u16[1], uint16_t); return z.f; } float ABCD_to_DCBA_float(float x) { uint32_t u32; memcpy(&u32,&x, 4); u32 = __builtin_bswap32(u32); memcpy(&x, &u32, 4); return x; } 

and it will generate the most efficient code:

ABCD_to_CDAB_float: movd eax, xmm0 mov ecx, eax shr eax, 16 mov edx, eax rol cx, 8 rol dx, 8 sal ecx, 16 movzx eax, dx or eax, ecx movd xmm0, eax ret ABCD_to_DCBA_float: movd eax, xmm0 bswap eax movd xmm0, eax ret 

Comments

0

You can do this with unions:

union f2l { float f; uint32_t l; }; float ABCD_to_CDAB(float input) { f2l t1; t1.f = input; uint16_t cd = t1.l >> 16; uint16_t ab = t1.l & 0x0000FFFF; t1.l = (ab << 16) | cd; return t1.f; } 

the 2 variables cd and ab are for sake of clarity and can be removed.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.