Gio
Gio

Reputation: 4229

Conversion of Single to two UInt16 values in .NET

In the good old days of C, I could cast a float to an int (assuming a 32-bit system), do some bit manipulation (bitwise and, right shift, etc.), and get the upper and lower 16 bit hex representations of the floating point number, which I could then store in two short values. I'm not seeing an easy way of doing this in C#.

System.Convert.ToUInt16 just does a float to int convert (even after I shift right), which leaves a value of 0 if the float is less than 0, which is not the desired effect.

//Useless leaves me with a value of 0
UIN16 s1 = (UInt16)((System.Convert.ToUInt32(d2) & 0xffff0000) >> 16);   //Capture the high word
UInt16 s2 = (UInt16)(System.Convert.ToUInt32(d2) & 0xffff);              //Capture the low word

A basic cast (UInt32) doesn't work either.

Upvotes: 1

Views: 3830

Answers (4)

Maciej Hehl
Maciej Hehl

Reputation: 7995

There is the class BitConverter which has some nice and useful methods.

For example, the method GetBytes can convert variables of many different types into a byte array, and the method ToUInt16 returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array.

Upvotes: 0

Nils Pipenbrinck
Nils Pipenbrinck

Reputation: 86393

Short answer: Don't do it!

Long explanation: In the good old days these hacks have been done because doing the float to int conversion (and vice versa) was a lot faster than letting the FPU do the job. I could be wrong, but I think I remember a success story of such an optimization for the PC game Descent (anyone remember it? - good old Quake 1 days). In this case it made 30% of a difference. At least so I remember..

Nowadays this is not true anymore, in fact the opposite is true. If you compile your code against the legacy x86 FPU, any bit-fiddling must be done in the integer unit, and each transition from the float to int unit must be done via memory. This cost a lot of cycles. You usually don't mix float and int operations of the same value, and the CPU is not optimized for this use-case.

If, on the other hand, the code gets compiled against the SSE instruction set you could take a shortcut. There is no extra cost to do integer operations on float data or vice versa, but straight int <-> float conversions via SSE is faster at the first place, so why bother?

It gets much worse with other architectures.. On the ARM Cortex-A8 for examples such tricks will stall the entire CPU for at least 21 cycles... I've read horror stories about mixing int and float on the PowerPC as well (load/modify/store anyone?)

In summary: Don't do it unless you write for Pentium-I legacy hardware. If so, contact me; I still have all the timings and example code on my hard disk drive.

Upvotes: 2

Reed Copsey
Reed Copsey

Reputation: 564681

The closest direct equivalent to your C method would be to do this with unsafe code, and pointers:

private static unsafe void SplitToWords(float f, out ushort s1, out ushort s2)
{
    unsafe
    {
        float* ptr = &f;
        UInt16* s2ptr = (UInt16*) ptr;
        UInt16* s1ptr = s2ptr + 1;

        s1 = *s1ptr;
        s2 = *s2ptr;
    }
}

This lets you do:

public static void Main()
{
    float f = -23984.123f;
    ushort s1;
    ushort s2;
    SplitToWords(f, out s1, out s2);

    Console.WriteLine("{0} : {1}/{2}", f, s1, s2);
    Console.ReadKey();
}

However, the more common managed way would be to use BitConverter.

byte[] fBytes = BitConverter.GetBytes(f);
s1 = BitConverter.ToUInt16(fBytes, 2);
s2 = BitConverter.ToUInt16(fBytes, 0);

You can see these produce the same thing here:

public static void Main()
{
    float f = -23984.123f;
    ushort s1;
    ushort s2;
    SplitToWords(f, out s1, out s2);

    Console.WriteLine("{0} : {1}/{2}", f, s1, s2);

    byte[] fBytes = BitConverter.GetBytes(f);
    s2 = BitConverter.ToUInt16(fBytes, 0);
    s1 = BitConverter.ToUInt16(fBytes, 2);
    Console.WriteLine("{0} : {1}/{2}", f, s1, s2);

    Console.ReadKey();
}

Upvotes: 1

Daniel Renshaw
Daniel Renshaw

Reputation: 34187

I think you're after the BitConverter class.

http://msdn.microsoft.com/en-us/library/system.bitconverter.aspx

        float value = 1.1f;
        byte[] bytes = BitConverter.GetBytes(value);
        short upper = BitConverter.ToInt16(bytes, 0);
        short lower = BitConverter.ToInt16(bytes, 2);

Upvotes: 7

Related Questions