Byte swapping with 32-Bit IEEE Floats...

  • Thread starter Thread starter Alhexx
  • Start date Start date
Status
Not open for further replies.
A

Alhexx

Guest
Well, I've written a VC++ DLL for Ultima to swap bytes (especially for the LWO exporter). Swapping integers was no problem, I simply used the << and >> operators. But they don't work with floats...

However, Alhexx went to write a bit of assembler code :D :
Code: [Select]
Code:
// Swaps Bytes in 32-Bit IEEE Floatfloat APIENTRY SwapFloat(float *Value){ float fretVal = float(0); float* pretVal = &fretVal; // Uses VC++ Inline Assembler _asm { // Push Registers to Stack (save them)  push eax  push ebx  push ecx  push edx  // Write Address of Values to ECX / EDX Register  mov ecx, Value  mov edx, pretVal  // Read 8-Bit Values from the Float and write them to AX / BX Registers  mov al, byte ptr [ecx]  mov ah, byte ptr [ecx + 1]  mov bl, byte ptr [ecx + 2]  mov bh, byte ptr [ecx + 3]  // Write them to the memory of the return Value  mov byte ptr [edx]    , bh  mov byte ptr [edx + 1], bl  mov byte ptr [edx + 2], ah  mov byte ptr [edx + 3], al  // Pop Registers from Stack  pop edx  pop ecx  pop ebx  pop eax } return fretVal;}

Any suggestions or better ideas?

 - Alhexx
 
Sure. Use typecasting ;)

Delphi and C (and therefore C++) both support it ...

In C I think (can't say for sure...) it'd be:

float somef;
int temp;

somef = value;

temp = (int)somef;

temp = (temp >> 24) | ( (temp >> 8) & 0xff00) | ( (temp << 8) & 0xff0000) | ( (temp << 24) & 0xff000000;

somef = (float)temp;


Well, you get the idea. Just tell it what type to interpret those values as. Obviously, works best when the source & destination variable are the same size...

In Delphi, two ways:

var
S:   Single;
I:    Integer;
begin
I := Integer(S);
//do some stuff with I...
S := Single(I);
end;


Or an even more compact way:

var
S:  Single
I:   Integer absolute S;

With that declaration, variables I and S occupy the same place in memory so you don't even need to typecast ;)
 
float somef;
int temp;
somef = value;
temp = (int)somef;
Im not sure about that, but i think that it will put only integer part of float number into temp ... and that is not what we wanted.

I would do it like this:

Code: [Select]
Code:
float SwapFloat( float fin ){  union {    float  f;    unsigned char c[4];  } normal, swapped;  normal.f = fin;  swapped.c[0] = normal.c[3];  swapped.c[1] = normal.c[2];  swapped.c[2] = normal.c[1];  swapped.c[3] = normal.c[0];  return swapped.f;}

Union is a good thingie, because all variables in it occupy same place. so c[0] is first byte of f, c[1] is 2nd ...

Or here is a more general function that i use, it is good for reversing anything (to 64bytes in length):

Code: [Select]
Code:
#define MaxSwapBytes 64void Swap( void* data, int LengthInBytes ){  assert( ((LengthInBytes > 0 ) && (LengthInBytes <= MaxSwapBytes )) );  if ( ! ((LengthInBytes > 0 ) && (LengthInBytes <= MaxSwapBytes )) )     return;  int    i;  unsigned char *normal, swapped[ MaxSwapBytes ];   normal = (unsigned char*) data;  for( i=0; i<LengthInBytes; i++ )      swapped[ i ] = normal[ LengthInBytes - 1 - i ];  memcpy( data, swapped, LengthInBytes );}
you have to supply a pointer to your variable and length in bytes, like swap( &f, sizeof( float ) ); or swap( &myshort, 2 );

It does the same as the one above, but in a cycle.
 
I'm pretty sure my way would work - that is, after all, the whole point about typecasting.
 
Fice: I'm not sure 'bout that method, too. In my opinion it'll only convert the integer part (as mirex said).

mirex: Ehm ... fine ... however, if I take a look at your second code, I think I'll prefer the ASM method :D

 - Alhexx
 
Hmm ... I dunno. I know you /can/ typecast in C, but I'll admit I'm not 100% on the syntax, since I don't really use C/C++ much ;)

I guess the alternative is to use pointers...

float f = somevalue;
int* ipoint = &f;


Then now perform all your bitswapping stuff on (*i) ...
 
ficedula: I agree with the other boys here, (int)float only uses the integer part, that is, the C compiler usually replaces it with a call to the _ftol (float to long) function.
 
How blade .... how /do/ you tell it to reinterpret as another type? Or do you have to use pointers to do that?
 
Pointers, or unions. Pointers may be easier for the most part.
 
I forgot to write it back then. So to retype float to unsigned long (both 4 bytes):
Code: [Select]
Code:
unsigned long  *ul;float          f;ul = (unsigned long*)&f;  //now here it is, but it is in pointer, so usage has to be like this://and if you change *ul, you change 'f' too, of course.*ul = *ul + 1000000;//or another way, not by retyping, it just moves float to long, easy one, but not too prettyunsigned long  ul;float          f;memcpy( &ul, &f, 4 );ul += 10000000;memcpy( &f, &ul, 4 );

mirex: Ehm ... fine ... however, if I take a look at your second code, I think I'll prefer the ASM method
You know, general functions allways look messy, and it would look even worse in asm.
 
Then again, why not copy it into its own stack space immediately, instead of pointers?
Code: [Select]
Code:
unsigned long ul;float f;ul = *(unsigned long*)&f;// After which, you can doul = ul + 10000;// Note that changing ul does *not* change f
 
Don't pass endian-swapped floats in as floats -- they will get loaded in and out of the floating point registers, and if the swapped bit pattern happens to correspond to a denormal, the FPU will normalize it and trash the value.

Code: [Select]
Code:
void __declspec(naked) __stdcall ByteSwap32(void *pValue) {    __asm {        mov edx, [esp+4]        mov eax, [edx]        bswap eax   ;requires 486+        mov [edx], eax        ret 4    }}
[/code]
 
Status
Not open for further replies.
Back
Top