How to convert an array of signed bytes to float? - c#

I just got confused about how to convert an array of 4 signed bytes to a float number.
I just know for an array of unsigned bytes bts, probably I can use this function
BitConverter.ToSingle(bts, 0);
However, it looks like BitConverter.ToSingle only accepts byte array instead of sbyte array.
Could somebody give me some ideas please?
Thanks!

Maybe this:
float num = 0;
for (int i = 0; i < sbytesArr.Length; i++)
{
num = (num | sbytesArr[i]) << i * 4;
}

Float value = 5000.1234;
//
// Invoke BitConverter.GetBytes to convert double to bytes.
//
byte[] array = BitConverter.GetBytes(value);
foreach (byte element in array)
{
Console.WriteLine(element);
}
//
// You can convert the bytes back to a double.
//
Float result = BitConverter.Tofloat(array, 0);
Console.WriteLine(result);

Assuming that your signed bytes are in an array named sbts you can first of all convert to an unsigned byte array, and then use BitConverter.ToSingle().
byte[] bts = new byte[sbts.Length];
Buffer.BlockCopy(sbts, 0, bts, 0, sbts.Length);
float f = BitConverter.ToSingle(bts, 0);

It is a little known fact that byte and sbyte are interchangeable at the CLR level:
sbyte[] a = new sbyte[1];
byte[] b = (byte[])(object)a;
This code actually works at runtime. So can pass in the array that you have.
BitConverter.ToSingle((byte[])(object)bts, 0);

Call GetFloatValue method passing una array of four sbyte as parameter
public float GetFloatValue(sbyte[] data)
{
return bytesToFloat(data[0], data[1], data[2], data[3]);
}
private static float bytesToFloat(sbyte b0, sbyte b1, sbyte b2, sbyte b3)
{
int mantissa = (byte)b0 + ((byte)b1 << 8) + ((byte)b2 << 16);
return (float)(mantissa * Math.Pow(10, b3));
}

Related

Representation of negative numbers in Hexadecimal format using C#

I am stuck at typical problem of conversion of data formats in WPF application in C#. I am new to C#. The GUI i am working on is displaying temperatures after getting the bytes data from the UDS messages.
When the data received in the format like 41c00000( which is in float format), the below function is able to convert the data into temperature in Celsius (both negative and positive).
public static float ComposeFloat(List<string> list, bool bigEndian)
{
float val;
byte[] payload = { 0, 0, 0, 0 }; // Used for float conversions
// BitConverter needs a little endian list
if (bigEndian)
{
list.Reverse();
}
for (int i = 0; i < 4; i++)
{
payload[i] = Convert.ToByte(list[i], 16);
}
val = BitConverter.ToSingle(payload, 0); // convert to an ieee754 float
return val;
}
But for some other temperature sensor in our system, the UDS is giving the data in the format 00000028. Since the UDS message was giving the temperature in integer format, I modified the above code as shown below, completely ignoring the case about what will happen if the temperature would be negative, which is really big mistake.
public static float ComposeFloat_FromInt(List<string> list, bool bigEndian)
{
float val;
int[] payload = { 0, 0, 0, 0 };
if (bigEndian)
{
list.Reverse();
}
for (int i = 0; i < 4; i++)
{
payload[i] = Convert.ToInt32(list[i], 16);
}
val = (float)payload[0];
return val;
}
Please guide me what will be the data received from the system when temperature is negative by giving some example and how should i modify the function to cover the negative temperature case.
Assuming the system sends temperatures as 32-bit signed integers using two's complement, you can use BitConverter.ToInt32 method to convert the array directly to a signed integer:
public static int ComposeFloat_FromInt(List<string> list, bool bigEndian)
{
int val;
byte[] payload = { 0, 0, 0, 0 };
if (bigEndian)
{
list.Reverse();
}
for (int i = 0; i < 4; i++)
{
payload[i] = Convert.ToByte(list[i], 16);
}
val = BitConverter.ToInt32(payload, 0); // Converts a byte array to Int32
return val;
}

converting hex to float 32

I have a hexadecimal (42 E6 56 00) which should be translated into this float number: 115.2, of course it is float 32 (I've got 115.2 using a calculator). Now I want to perform this operation using C#, I use following code but it gives me strange values:
byte[] bytes = BitConverter.GetBytes(0x42E65600);
if (BitConverter.IsLittleEndian) {
bytes = bytes.Reverse().ToArray();
}
float myFloat = BitConverter.ToSingle(bytes, 0);
The better way to write:
byte[] bytes = new byte[]{ 0x42, 0xE6, 0x56, 0x00 }; // Big endian data
if (BitConverter.IsLittleEndian) {
Array.Reverse(bytes); // Convert big endian to little endian
}
float myFloat = BitConverter.ToSingle(bytes, 0);
Note that BitConverter uses the platform's endianess.
I tested with an IEEE-754 Analysis, it seems that your source data is really big endian, so this is the correct way to write.
You may not know, but BitConverter.GetBytes(0x42E65600); will get byte[]{ 0x00, 0x56, 0xE6, 0x42 } on little endian platform.
If you insists on writing a hexadecimal literal, you don't need to convert the endianess (because it will always be correct, see #George's comment)
byte[] bytes = BitConverter.GetBytes(0x42E65600);
float myFloat = BitConverter.ToSingle(bytes, 0); // Always be correct
I have a hexadecimal (42 E6 56 00) which should be translated into
this float number: 115.2
If that is the case then your data is in big Endian format
BitConverter.IsLittleEndian
This will determine what your bit converted bitness is so you would want the condition to be
if (!BitConverter.IsLittleEndian)
Finally, your endian conversion is wrong. You would not want to reverse the entire array but swap the individual float entries
for (int i = 0; i < data.Length / 2; i++)
{
Swap<byte>(ref data[i], ref data[data.Length - i - 1]);
}
Combining all these rectification, your code should look something like
static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
static byte[] Big2Little(byte[] data)
{
for (int i = 0; i < data.Length / 2; i++)
{
Swap<byte>(ref data[i], ref data[data.Length - i - 1]);
}
return data;
}
static void Main(string[] args)
{
byte[] bytes = BitConverter.GetBytes(0x42E65600);
if (!BitConverter.IsLittleEndian)
{
bytes = Big2Little(bytes);
}
float myFloat = BitConverter.ToSingle(bytes, 0);
System.Console.Out.WriteLine(myFloat);
}
Note You can verify your result from the IEEE Analyzer

Store float as bytes into large array instead of new byte[]

I need to serialize a bunch of floats and convert to little endian if necessary. I'm aware of BitConverter.GetBytes(float), but I'd rather avoid allocating a ton of little 4-byte arrays on the GC heap. How can I do the conversion into an existing large byte[] array with an offset index? I want something like:
float[] theFloats; // filled up somewhere
byte[] theBytes = new byte[theFloats.Length * 4];
int offset = 0;
for (int i = 0; i < numFloats; ++i)
{
MagicClass.CopyFloatToBytes(theFloats[i], theBytes, offset);
offset += 4;
}
You can create a MemoryStream around the array, then create a BinaryWriter and write floats to it.
Why not just use BitConverter.GetBytes?
You can also do this with [StructLayout(LayoutKind.Explicit)]
[StructLayout(LayoutKind.Explicit)]
public struct Convert32BitType
{
[FieldOffset(0)]
public int Int32Value;
[FieldOffset(0)]
public float FloatValue;
}
// Example:
var tmp = new Convert32BitType();
tmp.FloatValue = 1.1;
int ival = tmp.Int32Value;
byte b1 = (byte)(ival >> 24);
byte b2 = (byte)(ival >> 16);
byte b3 = (byte)(ival >> 8);
byte b4 = (byte)(ival >> 0);
Another possibility is to used the fixed keyword and cast the pointer, but that requires unsafe code.

Converting 3 bytes into signed integer in C#

I'm trying to convert 3 bytes to signed integer (Big-endian) in C#.
I've tried to use BitConverter.ToInt32 method, but my problem is what value should have the lats byte.
Can anybody suggest me how can I do it in different way?
I also need to convert 5 (or 6 or 7) bytes to signed long, is there any general rule how to do it?
Thanks in advance for any help.
As a last resort you could always shift+add yourself:
byte b1, b2, b3;
int r = b1 << 16 | b2 << 8 | b3;
Just swap b1/b2/b3 until you have the desired result.
On second thought, this will never produce negative values.
What result do you want when the msb >= 0x80 ?
Part 2, brute force sign extension:
private static int Bytes2Int(byte b1, byte b2, byte b3)
{
int r = 0;
byte b0 = 0xff;
if ((b1 & 0x80) != 0) r |= b0 << 24;
r |= b1 << 16;
r |= b2 << 8;
r |= b3;
return r;
}
I've tested this with:
byte[] bytes = BitConverter.GetBytes(p);
int r = Bytes2Int(bytes[2], bytes[1], bytes[0]);
Console.WriteLine("{0} == {1}", p, r);
for several p.
The last value should be 0 if it isn't set for a positive number, 256 for a negative.
To know what you should pass in, you can try converting it the other way:
var bytes = BitConverter.GetBytes(i);
int x = BitConverter.ToInt32(bytes, 0);
To add to the existing answers here, there's a bit of a gotcha in that Bitconverter.ToInt32() will throw an ArgumentException if the array is less than sizseof(int) (4) bytes in size;
Destination array is not long enough to copy all the items in the collection. Check array index and length.
Given an array less than sizeof(int) (4) bytes in size, you can compensate for left/right padding like so;
Right-pad
Results in positive Int32 numbers
int intByteSize = sizeof(int);
byte[] padded = new byte[intByteSize];
Array.Copy(sourceBytes, 0, padded, 0, sourceBytes.Length);
sourceBytes = padded;
Left-pad
Results in negative Int32 numbers, assuming non-zero value at byte index sourceBytes.Length - 1.
int intByteSize = sizeof(int);
byte[] padded = new byte[intByteSize];
Array.Copy(sourceBytes, 0, padded, intByteSize - sourceBytes.Length, sourceBytes.Length);
sourceBytes = padded;
Once padded, you can safely call int myValue = BitConverter.ToInt32(sourceBytes, 0);.

Fast way to swap bytes in array from big endian to little endian in C#

I'm reading from a binary stream which is big-endian. The BitConverter class does this automatically. Unfortunately, the floating point conversion I need is not the same as BitConverter.ToSingle(byte[]) so I have my own routine from a co-worker. But the input byte[] needs to be in little-endian. Does anyone have a fast way to convert endianness of a byte[] array. Sure, I could swap each byte but there has got to be a trick. Thanks.
Here is a fast method for changing endianess for singles in a byte array:
public static unsafe void SwapSingles(byte[] data) {
int cnt = data.Length / 4;
fixed (byte* d = data) {
byte* p = d;
while (cnt-- > 0) {
byte a = *p;
p++;
byte b = *p;
*p = *(p + 1);
p++;
*p = b;
p++;
*(p - 3) = *p;
*p = a;
p++;
}
}
}
I use LINQ:
var bytes = new byte[] {0, 0, 0, 1};
var littleEndianBytes = bytes.Reverse().ToArray();
Single x = BitConverter.ToSingle(littleEndianBytes, 0);
You can also .Skip() and .Take() to your heart's content, or else use an index in the BitConverter methods.
What does the routine from your co-worker look like? If it accesses the bytes explicitly, you could change the code (or rather, create a separate method for big-endian data) instead of reversing the bytes.

Categories

Resources