220

I need to convert an int to a byte[] one way of doing it is to use BitConverter.GetBytes(). But im unsure if that matches the following specification:

An XDR signed integer is a 32-bit datum that encodes an integer in the range [-2147483648,2147483647]. The integer is represented in two's complement notation. The most and least significant bytes are 0 and 3, respectively. Integers are declared as follows:

Source: RFC1014 3.2

How could i do a int to byte transformation that would satisfy the above specification?

1
  • If you want more general information about various methods of representing numbers including Two's Complement have a look at: Two's Complement and Signed Number Representation on Wikipedia Commented Aug 23, 2009 at 16:39

10 Answers 10

276

The RFC is just trying to say that a signed integer is a normal 4-byte integer with bytes ordered in a big-endian way.

Now, you are most probably working on a little-endian machine and BitConverter.GetBytes() will give you the byte[] reversed. So you could try:

int intValue;
byte[] intBytes = BitConverter.GetBytes(intValue);
Array.Reverse(intBytes);
byte[] result = intBytes;

For the code to be most portable, however, you can do it like this:

int intValue;
byte[] intBytes = BitConverter.GetBytes(intValue);
if (BitConverter.IsLittleEndian)
    Array.Reverse(intBytes);
byte[] result = intBytes;
Sign up to request clarification or add additional context in comments.

3 Comments

Or use ToArray. byte[] result = BitConverter.GetBytes(intValue).Reverse().ToArray();
why the last line byte[] result = intBytes; ? isn't intBytes already the array you want?
@derHugo That's correct, result is redundant in this code piece. However, I felt that it was more pedagogical to explicitly show the reader what the result was than assuming that they can figure out that the result is contained in some variable named intBytes. Besides, doing the assignment is cheap since it does not copy the memory nor allocate new memory, it just adds a new name to the already allocated array. So why not do it?
56

Here's another way to do it: as we all know 1x byte = 8x bits and also, a "regular" integer (int32) contains 32 bits (4 bytes). We can use the >> operator to shift bits right (>> operator does not change value.)

int intValue = 566;

byte[] bytes = new byte[4];

bytes[0] = (byte)(intValue >> 24);
bytes[1] = (byte)(intValue >> 16);
bytes[2] = (byte)(intValue >> 8);
bytes[3] = (byte)intValue;

Console.WriteLine("{0} breaks down to : {1} {2} {3} {4}",
    intValue, bytes[0], bytes[1], bytes[2], bytes[3]);

5 Comments

The array initializer and the xor (^) and & 0xFF bits are unnecessary.
It's big-endian, so MSB is stored first, so you should inverse your indices.
Can we add an example of going the opposite way? (bytes back to integer)
I use this because of performance. If I don't care about this small difference, I will go for the accepted answer.
You should wrap that code in an unchecked block. Currently it only works if integer overflow checking is disabled in the compiler settings.
30

BitConverter.GetBytes(int) almost does what you want, except the endianness is wrong.

You can use the IPAddress.HostToNetwork method to swap the bytes within the the integer value before using BitConverter.GetBytes or use Jon Skeet's EndianBitConverter class. Both methods do the right thing(tm) regarding portability.

int value;
byte[] bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value));

Comments

26

The other way is to use BinaryPrimitives like so

byte[] intBytes = BitConverter.GetBytes(123); int actual = BinaryPrimitives.ReadInt32LittleEndian(intBytes);

4 Comments

This should be top answer, but consider adding: learn.microsoft.com/en-us/dotnet/api/…
This should be the accepted answer. But beware - it's available starting from .NET 5.0 and .NET Core 2.1.
whats with the '123'?
Just an example - "int actual" should end up as 123.
7

Why all this code in the samples above...

A struct with explicit layout acts both ways and has no performance hit.

Update: Since there's a question on how to deal with endianness I added an interface that illustrates how to abstract that. Another implementing struct can deal with the opposite case

public interface IIntToByte
{
    Int32 Int { get; set;}

    byte B0 { get; }
    byte B1 { get; }
    byte B2 { get; }
    byte B3 { get; }
}

[StructLayout(LayoutKind.Explicit)]
public struct IntToByteLE : UserQuery.IIntToByte
{
    [FieldOffset(0)]
    public Int32 IntVal;

    [FieldOffset(0)]
    public byte b0;
    [FieldOffset(1)]
    public byte b1;
    [FieldOffset(2)]
    public byte b2;
    [FieldOffset(3)]
    public byte b3;

    public Int32 Int {
        get{ return IntVal; }
        set{ IntVal = value;}
    }

    public byte B0 => b0;
    public byte B1 => b1;
    public byte B2 => b2;
    public byte B3 => b3; 
}

5 Comments

How would you use this? and secondly would you get the same result even is you run this on 'big-endian' and on 'little-endian'.
@Peter here's an update. It should still perform better than shifts
yup. Basically a c++ union implemented as c# structs. This is super-fast and is good for packing and unpacking all kinds of things that don't fit nicely into the bitconverter/endian problem solutions. IIRC, NAudio uses this approach to very good effect.
Not a bad idea, but I would of made it a byte array instead of each byte individually. Might just be me though.
@Chizl that would cause indexing for each individual byte, and indexing is slower... although I haven't tested perf difference, so I may be wrong
6

In .NET Core 2.1+ and .NET 5+ you can use BinaryPrimitives.WriteInt32BigEndian() like this:

int intVal = ...;
byte[] bytes = new byte[4];
BinaryPrimitives.WriteInt32BigEndian(bytes, intVal);
  • The conversion is done in one line.
  • It is a built in function.
  • It does not depend on the endianness of the computer.

Comments

3

When I look at this description, I have a feeling, that this xdr integer is just a big-endian "standard" integer, but it's expressed in the most obfuscated way. Two's complement notation is better know as U2, and it's what we are using on today's processors. The byte order indicates that it's a big-endian notation.
So, answering your question, you should inverse elements in your array (0 <--> 3, 1 <-->2), as they are encoded in little-endian. Just to make sure, you should first check BitConverter.IsLittleEndian to see on what machine you are running.

Comments

0
using static System.Console;

namespace IntToBits
{
    class Program
    {
        static void Main()
        {
            while (true)
            {
                string s = Console.ReadLine();
                Clear();
                uint i;
                bool b = UInt32.TryParse(s, out i);
                if (b) IntPrinter(i);
            }
        }

        static void IntPrinter(uint i)
        {
            int[] iarr = new int [32];
            Write("[");
            for (int j = 0; j < 32; j++)
            {
                uint tmp = i & (uint)Math.Pow(2, j);

                iarr[j] = (int)(tmp >> j);
            }
            for (int j = 32; j > 0; j--)
            {
                if(j%8==0 && j != 32)Write("|");
                if(j%4==0 && j%8 !=0) Write("'");
                Write(iarr[j-1]);
            }
            WriteLine("]");
        }
    }
}```

Comments

0

I wouldn’t recommend using byte arrays to get bytes from integers stored in the stack. It produces an array that will be stored in the heap until the next GC clean up. I prefer using spans for these simple tasks.

So my solution is here to use MemoryMarshal.AsBytes<T>(ReadOnlySpan<T>). You don't need to worry about the endianness, about casting. You just read the method name and understand that this will return bytes from any type of span. You just wrap your integer into a span via MemoryMarshal.CreateReadOnlySpan<T>(T, Int32). Those bytes will be stored in the stack and terminated after leaving the scope.

Full solution will be:

var integer = int.MaxValue;

var readOnlySpan = MemoryMarshal.CreateReadOnlySpan(ref integer, length: 1);
var bytes = MemoryMarshal.AsBytes(readOnlySpan);

Note that AsBytes provides raw representation of the value. You can accidentally change the span and it will be reflected on the integer itself. So don't forget to always use read-only spans when you don't want to change anything inside.

5 Comments

Part of why this answer may not have been provided previously is that the question is fifteen years old, but CreateReadOnlySpan wasn’t introduced until .NET 4.7.
@JeremyCaney I guess You meant .NET Framework 4.7
Correct. When this question was asked, there wasn’t a .NET Core or .NET. It was all .NET Framework.
This seems far superior perfomance wise :), but I would still argue that you need to know about the endianness as there is no guarantee that it data you are reading has the same as your current platform?
@Peter It depends on what you're doing. I mean the question was about getting bytes from a single integer. I can't see any case where you need big endian for that scenario
-2
byte[] Take_Byte_Arr_From_Int(Int64 Source_Num)
{
   Int64 Int64_Num = Source_Num;
   byte Byte_Num;
   byte[] Byte_Arr = new byte[8];
   for (int i = 0; i < 8; i++)
   {
      if (Source_Num > 255)
      {
         Int64_Num = Source_Num / 256;
         Byte_Num = (byte)(Source_Num - Int64_Num * 256);
      }
      else
      {
         Byte_Num = (byte)Int64_Num;
         Int64_Num = 0;
      }
      Byte_Arr[i] = Byte_Num;
      Source_Num = Int64_Num;
   }
   return (Byte_Arr);
}

4 Comments

please add more explanation
Thanks for the answer, why did you choose to write a new implementation instead of using the stackoverflow.com/a/1318948/58553 ? (are there any pro or cons with your solution)
In case when project need to be developed with not only one language, it can be useful realize code similarity between languages in situation when identical ideas must be implemented. This way may help with mistakes catching. Sure using ‘c#’ function ‘BitConverter.GetBytes’ with ‘Endian’ checking that all what needed in most cases. And more, in some situation may happened use conversion not only to Int32(4 bytes) or Int64(8 bytes) but to other number of bytes. Thanks.
This isn't really universally usable at all... it always returns an 8-byte array, which brings a lot of extra work with input values that aren't 64-bit ints. I'd logically want a 2-byte array when converting an Int16.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.