2

Is it possible to convert floats from big to little endian? I have a value from a big endian platform that I am sending via UDP to a Windows process (little endian). This value is a float, but when I am trying BitConverter.ToSingle I always get 5.832204E-42, but it should be 36.000.

What am I doing wrong?

Here is a code snipped:

// Receive thread
private void ReceiveData()
{
    int count = 0;

    IPEndPoint remoteIP = new IPEndPoint(IPAddress.Parse("10.0.2.213"), port);
    client = new UdpClient(remoteIP);
    while (true)
    {
        try
        {
            IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);
            byte[] data = client.Receive(ref anyIP);

            float x = BitConverter.ToSingle(data, 0);
            float y = BitConverter.ToSingle(data, 4);
            float z = BitConverter.ToSingle(data, 8);
            float alpha = BitConverter.ToSingle (data, 12);
            float theta = BitConverter.ToSingle (data, 16);
            float phi = BitConverter.ToSingle (data, 20);

            print(">> " + x.ToString() + ", "+ y.ToString() + ", "+ z.ToString() + ", " +
                  alpha.ToString() + ", "+ theta.ToString() + ", "+ phi.ToString());

            // Latest UDP packet
            lastReceivedUDPPacket=x.ToString()+" Packet#: "+count.ToString();
            count = count+1;
        }
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Termin
  • 151
  • 5
  • 13

1 Answers1

5

36.0 and 5.832204E-42 are endian-opposites, so this is an endianness problem. On Windows / .NET, you are usually little-endian, so I'm guessing the data is big-endian. That means you need to reverse the data for each value, separately (not the entire array).

To write this in a way that is CPU-safe, the best option is to check BitConverter.IsLittleEndian, and compensate when necessary. For example:

public static float ReadSingleBigEndian(byte[] data, int offset)
{
    return ReadSingle(data, offset, false);
}
public static float ReadSingleLittleEndian(byte[] data, int offset)
{
    return ReadSingle(data, offset, true);
}
private static float ReadSingle(byte[] data, int offset, bool littleEndian)
{
    if (BitConverter.IsLittleEndian != littleEndian)
    {   // other-endian; reverse this portion of the data (4 bytes)
        byte tmp = data[offset];
        data[offset] = data[offset + 3];
        data[offset + 3] = tmp;
        tmp = data[offset + 1];
        data[offset + 1] = data[offset + 2];
        data[offset + 2] = tmp;
    }
    return BitConverter.ToSingle(data, offset);
}

with:

float x= ReadSingleBigEndian(data, 0);
float y= ReadSingleBigEndian(data, 4);
float z= ReadSingleBigEndian(data, 8);
float alpha= ReadSingleBigEndian(data, 12);
float theta= ReadSingleBigEndian(data, 16);
float phi= ReadSingleBigEndian(data, 20);
Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 2
    Why not `Array.Reverse(data, offset, 4)`? – phoog Oct 24 '12 at 07:53
  • @phoog that would work, but does a **lot** of extra checks etc along the way (after all the checks, I *expect* it would be handled by the `extern` method, `TrySZReverse`). But yes, that would work. – Marc Gravell Oct 24 '12 at 07:57
  • Isn't `ReadSingle()` destructive; that is, won't it reverse bytes in the *original* array that is passed in? – Robert Harvey Sep 07 '18 at 20:40
  • @RobertHarvey yes, in this example it is; I wouldn't do it this way today - there's a big-endian and little-endian decoder in "buffers", IIRC, which the JIT can map to intrinsics - see `BinaryPrimitives` – Marc Gravell Sep 08 '18 at 00:14