Author Topic: Byte swapping with 32-Bit IEEE Floats...  (Read 10336 times)

Alhexx

  • *
  • Posts: 1894
    • View Profile
    • http://www.alhexx.com
Byte swapping with 32-Bit IEEE Floats...
« on: 2002-06-04 17:07:33 »
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]

// Swaps Bytes in 32-Bit IEEE Float
float 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

ficedula

  • *
  • Posts: 2178
    • View Profile
    • http://www.ficedula.co.uk
Byte swapping with 32-Bit IEEE Floats...
« Reply #1 on: 2002-06-04 23:03:00 »
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 ;)

mirex

  • *
  • Posts: 1645
    • View Profile
    • http://mirex.mypage.sk
Byte swapping with 32-Bit IEEE Floats...
« Reply #2 on: 2002-06-05 12:32:05 »
Quote
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]

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]

#define MaxSwapBytes 64
void 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.

ficedula

  • *
  • Posts: 2178
    • View Profile
    • http://www.ficedula.co.uk
Byte swapping with 32-Bit IEEE Floats...
« Reply #3 on: 2002-06-05 13:58:52 »
I'm pretty sure my way would work - that is, after all, the whole point about typecasting.

Alhexx

  • *
  • Posts: 1894
    • View Profile
    • http://www.alhexx.com
Byte swapping with 32-Bit IEEE Floats...
« Reply #4 on: 2002-06-05 16:22:08 »
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

ficedula

  • *
  • Posts: 2178
    • View Profile
    • http://www.ficedula.co.uk
Byte swapping with 32-Bit IEEE Floats...
« Reply #5 on: 2002-06-05 16:32:30 »
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) ...

Qhimm

  • Founder
  • *
  • Posts: 1996
    • View Profile
    • Qhimm.com
Byte swapping with 32-Bit IEEE Floats...
« Reply #6 on: 2002-06-05 16:34:05 »
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.

ficedula

  • *
  • Posts: 2178
    • View Profile
    • http://www.ficedula.co.uk
Byte swapping with 32-Bit IEEE Floats...
« Reply #7 on: 2002-06-05 16:38:13 »
How blade .... how /do/ you tell it to reinterpret as another type? Or do you have to use pointers to do that?

Qhimm

  • Founder
  • *
  • Posts: 1996
    • View Profile
    • Qhimm.com
Byte swapping with 32-Bit IEEE Floats...
« Reply #8 on: 2002-06-05 16:43:53 »
Pointers, or unions. Pointers may be easier for the most part.

mirex

  • *
  • Posts: 1645
    • View Profile
    • http://mirex.mypage.sk
Byte swapping with 32-Bit IEEE Floats...
« Reply #9 on: 2002-06-06 12:11:51 »
I forgot to write it back then. So to retype float to unsigned long (both 4 bytes):
Code: [Select]
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 pretty
unsigned long  ul;
float          f;

memcpy( &ul, &f, 4 );
ul += 10000000;
memcpy( &f, &ul, 4 );


Quote
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.

Qhimm

  • Founder
  • *
  • Posts: 1996
    • View Profile
    • Qhimm.com
Byte swapping with 32-Bit IEEE Floats...
« Reply #10 on: 2002-06-06 15:46:50 »
Then again, why not copy it into its own stack space immediately, instead of pointers?
Code: [Select]
unsigned long ul;
float f;

ul = *(unsigned long*)&f;

// After which, you can do
ul = ul + 10000;

// Note that changing ul does *not* change f

phaeron

  • *
  • Posts: 30
    • View Profile
Byte swapping with 32-Bit IEEE Floats...
« Reply #11 on: 2002-06-08 03:14:41 »
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]

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]