How can I convert a numerical value (e.g. float, short, int, ...) to several byte values without having to allocate memory on the heap for an array, like System.BitConverter.GetBytes does?
Something like this:
public static void GetBytes(short input, out byte byte0, out byte byte1)
{
//demo code, just to show which results I need
var bytes = System.BitConverter.GetBytes(input);
byte0 = bytes[0];
byte1 = bytes[1];
}
Note: I'm restricted to .NET Framework 4.8 and therefore (I think) C# 7.3.
Just cast and shift?
public static void GetBytes(short input, out byte byte0, out byte byte1)
{
byte0 = (byte)input;
byte1 = (byte)(input >> 8);
}
Note that you can simply reverse the order for different endianness.
Note that if you are using a "checked" context, you would need to mask too:
public static void GetBytes(short input, out byte byte0, out byte byte1)
{
byte0 = (byte)(input & 0xFF);
byte1 = (byte)((input >> 8) & 0xFF);
}
(in an unchecked context, the cast to byte is sufficient by itself - additional bits are discarded)
If you are allowed to use unsafe code, then for non-integral value types (such as float, double and decimal) the fastest way is to use a pointer.
For example, for a double:
double x = 123.456;
unsafe
{
byte* p = (byte*) &x;
// Doubles have 8 bytes.
byte b0 = *p++;
byte b1 = *p++;
byte b2 = *p++;
byte b3 = *p++;
byte b4 = *p++;
byte b5 = *p++;
byte b6 = *p++;
byte b7 = *p;
}
You should be able see how to modify this for other types.
For integral types such as short, int and long you could use the approach given in the other answer from Mark Gravell - but you can also use the pointer approach above for integral types.
IMPORTANT: When using the pointer approach, endianness is significant! The order in which the bytes are assigned is the order in which they are stored in memory, which can differ by processor architecture.
Related
I have this method to convert long to little endian byte array
public static byte[] UnsignedIntegerToLEByteArray(ulong value)
{
// Value in bytes... in your system's endianness (let's say: little endian)
byte[] bytes = BitConverter.GetBytes(value);
// If it was big endian, reverse it
if (!BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return bytes;
}
My goal is to use it for shorted data types too, like int, short, etc. See here:
byte a = 0xAA;
ushort b = 0xEEAA;
uint c = 0xAABBCCDD;
ulong d = 0xAABBCCDDAAAAAABB;
// If you passed short below, you are only interested
// in first two bytes of the array
byte []tmp = DppUtilities.UnsignedIntegerToLEByteArray(b);
This works if my machine is little endian.
Will it also work if it is run on big endian machine? I think yes, but I would like to verify.
You can play a trick with a help of IntPtr and Marshal in order to convert any struct (including byte, ushort and ulong):
// Disclaimer: the structure will be reversed as a whole, not field by field
public static byte[] ToLEByteArray<T>(T value) where T: struct {
int size = Marshal.SizeOf(typeof(T));
byte[] bytes = new byte[size];
IntPtr p = Marshal.AllocHGlobal(size);
try {
Marshal.StructureToPtr(value, p, true);
Marshal.Copy(p, bytes, 0, size);
}
finally {
Marshal.FreeHGlobal(p);
}
// If it was big endian, reverse it
if (!BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return bytes;
}
....
Byte b = 123;
ushort s = 123;
ulong l = 123;
Byte[] result_byte = ToLEByteArray(b);
Byte[] result_ushort = ToLEByteArray(s);
Byte[] result_ulong = ToLEByteArray(l);
....
int i = 123456;
Byte[] result_int = ToLEByteArray(i);
EDIT: what's wrong with the implementation in the question? (from the comment).
Or, restating the question, what is that stuff with IntPtr, Marshal for?
The main issue of the question's implementation is the initial conversion to ulong:
// all the arguments will be converted to ulong
public static byte[] UnsignedIntegerToLEByteArray(ulong value)
In order to illustrate the problem, imagine, that we have two values
Byte x = 0x12; // 18
ulong u = 0x0000000000000012; // 18
and the expected output is
new byte[] {0x12}; // for a single byte
new byte[] {0x12, 0, 0, 0, 0, 0, 0, 0}; // for 8 bytes, i.e. ulong
however, the actual output will be
new byte[] {0x12, 0, 0, 0, 0, 0, 0, 0};
for both byte and ulong. This misbehaviour can lead to problems if you, say, want to write down numeric values (byte, short, ulong etc.), to a binary file, pass them to a binary stream etc:
using (Stream stm = ...) {
...
Byte[] buffer = UnsignedIntegerToLEByteArray(...);
stm.Write(buffer, offset, buffer.Length); // <- the (possibly!) erroneous write
...
}
I had the same thing and I tested it.
I can tell you it works 100%. If you want to check it out yourself, you can for example just pretend you need it the other way. So you reverse the byte array and reverse it if it's IsLittleEndian.
Your output is always in LittleEndian in your method, which is what you want.
Like BitConverter.GetBytes has different overloads for that, you should do it the same way:
public static byte[] UnsignedIntegerToLEByteArray(ulong value) { ...
public static byte[] UnsignedIntegerToLEByteArray(int value) { ...
public static byte[] UnsignedIntegerToLEByteArray(short value) { ...
The complier chooses the right overload when using it like you already did:
byte []tmp = DppUtilities.UnsignedIntegerToLEByteArray(b);
If you try to do this with a single method that try to check the zero bytes, it will produce wrong results:
How to tell if the number 00 00 12 34 should reverse 2 or 4 bytes?
Say you have a byte variable, and you assign it the decimal value of 102.
(byte myByteVariable = 102;)
Why is that possible? Shouldn't you have to provide an actual 8 bit value instead to avoid confusion? And how do I set a byte value by supplying bits instead?
And how do I set a byte value by supplying bits instead?
If you are setting the byte value explicitly, you can do so using the prefix 0x to indicate that you are doing so in hexadecimal, as an encoding for groups of 4 bits. E.g. the hex value of decimal 102 is 0x66, which is equivalent to 0110 0110.
But for supplying bits directly, Marc Gravell provides an option here for converting from binary to int using the Convert class, and similarly you can convert the string representation of a binary value into a byte using the Convert.ToByte method as:
byte b = Convert.ToByte("01100110", 2);
byte is a decimal type which represents a decimal number, it doesn't represent a bit field. So 102 is a normal value for it, because it's in the range of byte values (which is [0;255]). If you want to manipulate the bits, consider using BitArray or BitVector32.
A byte can store 8 bits (that is values from 0 to 255). A short stores 16 bits, an int 32, etc. Each integer type in C# simply allows the storage of a wider range of numbers. C# allows you to assign a whole number to any of these types.
In order to set each bit individually, you will need to use bitwise operators.
I actually wrote a library a while ago to handle this, allowing you to set each bit of the byte using data[x]. It is very similar to the BitArray class (which I'm not sure if I knew about when I made it)
The main idea of it is:
private byte data;
public void SetBit(int index, bool value)
{
if (value)
data = (byte)(data | (1 << index));
else
data = (byte)(data & ~(1 << index));
}
public bool GetBit(int index)
{
return ((data & (1 << index)) != 0);
}
I'm receiving byte-arrays containing float variables (32 bit).
In my C# application I'd like to turn byte[] byteArray into a float using bitwise shifting (because it's a lot faster than BitConverter).
Turning a byte-array into a short works like this:
short shortVal = (short)((short)inputBuffer [i++] << 8 | inputBuffer [i++]);
How do I do this for float-variables?
Let's gut the BCL and use its intestines for our purposes:
unsafe public static float ToSingle (byte[] value, int startIndex)
{
int val = ToInt32(value, startIndex);
return *(float*)&val;
}
You can implement ToInt32 using bit shifting.
If you don't need endianness behavior a single unsafe access can give you the float (assuming it's aligned).
Alternatively, you can use a union struct to convert an int to a float.
To get away from C# conventional methods and obtain fast performance, you'll most likely have to implement "unsafe" behavior. You could do something like the C style memory copy.
unsafe public static void MemoryCopy (void* memFrom, void* memTo, int size) {
byte* pFrom = (byte*)memFrom;
byte* pTo = (byte*)memTo;
while (size-- >= 0)
*pTo++ = *pFrom++;
}
This assumes that the float's endianness is the same going into the byte[] as it on the other end.
To use this you'll have to first fix the byte array since the runtime can move it anytime it wants during garbage collection. Something like this:
float f;
unsafe {
fixed (byte* ptr = byteArray) {
MemoryCopy (ptr, &f, sizeof(float));
}
}
I want to convert a byte to an sbyte, without changing any bits.
byte b = 0x84;
sbyte sb = unchecked((sbyte)b);
Console.WriteLine("0x" + Convert.ToString(b, 16));
Console.WriteLine("0x" + Convert.ToString(sb, 16));
The result of this will be:
0x84
0xff84
I understand what the result means and why it happens. However, I cannot seem to find out what I can do to avoid this behaviour. How can I copy the actual binary value of a byte and get it inside an sbyte?
The bit's are not changing between b and sb at all. This behavior is coming from Convert.ToString(). There just isn't an overload of Convert.ToString() that takes an sbyte and a base. The closest match would be Convert.ToString Method (Int16, Int32). So sb is being sign extended to 16 bits.
Using ToString in general produce less surprising results and don't involce unexpected conversions (like sbyte -> short as described in shf301's answer).
byte b = 0x84;
sbyte sb = unchecked((sbyte)b);
Console.WriteLine("0x" + b.ToString("x"));
Console.WriteLine("0x" + sb.ToString("x"));
Or just use format directly:
String.Format("0x{0:x}",((unchecked ((sbyte)0x84))))
I have a C# NETMF project, and I need to convert a float to a byte[] and vice versa. The problem is, NETMF doesn't have System.BitConverter like .NET, so I can't really find any way of doing it without going low level and doing it myself.
I have always programmed high-level (Java, Python, C#, etc.) and have only dabbled in C++, so I don't really know how to turn a float into a byte array.
What would some sample code of a boilerplate function for doing this look like?
The StructLayoutAttribute is supported by the .NET Micro Framework, so you could use a C++ style union to get the bytes of a float (and the other way around too):
[StructLayout(LayoutKind.Explicit)]
public struct FloatUnion
{
[FieldOffset(0)] public float Value;
[FieldOffset(0)] public byte Byte0;
[FieldOffset(1)] public byte Byte1;
[FieldOffset(2)] public byte Byte2;
[FieldOffset(3)] public byte Byte3;
public byte[] ToByteArray()
{
return new[] { Byte0, Byte1, Byte2, Byte3 };
}
public static byte[] FloatToBytes(float value)
{
return new FloatUnion { Value = value }.ToByteArray();
}
public static float BytesToFloat(byte[] bytes)
{
if (bytes.Length != 4) throw new ArgumentException("You must provide four bytes.");
return new FloatUnion { Byte0 = bytes[0], Byte1 = bytes[1], Byte2 = bytes[2], Byte3 = bytes[3] }.Value;
}
}
Unlike the integers (Int32, Byte, UInt64, etc) where each value is easily determined from the status of each bit in the integer's bytes, there is no intuitive representation of a floating-point number in binary, so the IEEE defined a specification for floating point numbers: IEEE-754. Suffice to say, it isn't simple, but following the rules in the specification will allow you to serialize a .NET System.Single and System.Double instance to a series of bytes.
The desktop .NET Framework actually cheats here. The GetBytes(Single) function actually just casts the Single instance to an Int32 then copies the raw bytes into a 4-byte array using raw pointers. However you can't take this useful shortcut because the micro framework does not support pointers - even worse: the MF doesn't include BinaryWriter either. You'll have to roll your own IEEE-754 serializer I'm afraid.
Use a BinaryWriter on top of a MemoryStream.