How to Convert Int to Hex String with Leading Zeros in C#

To convert int to hex string with leading zeros in C#, call ToString("X4") and put the digit count you want after the X. Use a lowercase x for lowercase digits:

int value = 255;

Console.WriteLine(value.ToString("X4"));   // 00FF
Console.WriteLine(value.ToString("x8"));   // 000000ff
Console.WriteLine($"0x{value:X2}");        // 0xFF

That’s 255 printed as 00FF, 000000ff and 0xFF. I ran every example below on .NET 10, including the negative numbers and byte arrays that trip people up.

ToString(“X”) Syntax: How the Number After X Sets the Width

The X format specifier turns an integral value into hex digits. The number after it is the minimum digit count, and C# pads with zeros on the left to reach it. Same 255, five widths:

int value = 255;

Console.WriteLine($"X   -> {value.ToString("X")}");
Console.WriteLine($"X2  -> {value.ToString("X2")}");
Console.WriteLine($"X4  -> {value.ToString("X4")}");
Console.WriteLine($"X8  -> {value.ToString("X8")}");
Console.WriteLine($"x8  -> {value.ToString("x8")}");

int big = 48879;
Console.WriteLine($"48879 as X2 -> {big.ToString("X2")}");
Console.WriteLine($"10 as X2    -> {10.ToString("X2")}");

Output:

X   -> FF
X2  -> FF
X4  -> 00FF
X8  -> 000000FF
x8  -> 000000ff
48879 as X2 -> BEEF
10 as X2    -> 0A
VS Code running HexWidths.cs: C# int 255 formatted with ToString("X"), "X2", "X4", "X8" and "x8" as FF, 00FF, 000000FF and 000000ff
X alone gives FF; X4 and X8 add the leading zeros

Plain X gives FF with no padding. X4 gives 00FF and x8 gives 000000ff, so the case of the letter only changes A-F.

The width is a minimum, never a maximum: 48879 as X2 still prints BEEF. And 10 as X2 prints 0A, the usual two-digit byte style.

X works on every integral type in the C# data types list (byte, short, int, long). It throws FormatException on double or decimal, so cast first. The result doesn’t depend on the PC’s culture.

Hex with Leading Zeros in String Interpolation and string.Format

Inside a longer string, put the specifier after a colon: {value:X2}. Here’s the brand color of Lakeside Bakery, a made-up shop in Madison, Wisconsin, as a web color:

using System.Text;

// Lakeside Bakery (Madison, WI) brand color
int red = 0, green = 128, blue = 96;

Console.WriteLine($"#{red:X2}{green:X2}{blue:X2}");
Console.WriteLine(string.Format("#{0:X2}{1:X2}{2:X2}", red, green, blue));

int width = 6;
Console.WriteLine($"Order ID: 0x{4095.ToString($"X{width}")}");

var sb = new StringBuilder("Badge ");
sb.AppendFormat("{0:X4}", 255);
Console.WriteLine(sb);

Output:

#008060
#008060
Order ID: 0x000FFF
Badge 00FF
VS Code running HexInterpolation.cs: C# string interpolation {red:X2}{green:X2}{blue:X2} and string.Format build the color #008060
Interpolation and string.Format both pad each channel to two digits

Both lines print #008060. Without X2, the 0 channel would print as a single 0 and the color code would be wrong.

When the width comes from a variable, build the format string: $"X{width}" gives 0x000FFF for 4095. StringBuilder.AppendFormat takes the same {0:X4} item, so the badge prints as Badge 00FF.

It’s the same composite-format syntax you’d use to format numbers with commas in C#, just with X instead of N.

Convert.ToString(n, 16) with PadLeft for a Fixed Width

Convert.ToString(value, 16) also converts to base 16, but it returns lowercase digits with no padding option. Same 255:

int value = 255;

string raw = Convert.ToString(value, 16);
Console.WriteLine($"Convert.ToString: {raw}");
Console.WriteLine($"PadLeft(8, '0'):  {raw.PadLeft(8, '0')}");
Console.WriteLine($"Uppercase:        {raw.PadLeft(8, '0').ToUpperInvariant()}");
Console.WriteLine($"Extension method: {value.ToHex(6)}");

static class HexExtensions
{
    public static string ToHex(this int n, int digits) => n.ToString($"X{digits}");
}

Output:

Convert.ToString: ff
PadLeft(8, '0'):  000000ff
Uppercase:        000000FF
Extension method: 0000FF

You get ff, then 000000ff after PadLeft(8, '0'), then 000000FF after ToUpperInvariant. That’s three calls to do what ToString("X8") does in one.

If you format hex all over a codebase, a one-line extension method keeps the call short: value.ToHex(6) prints 0000FF. PadLeft is the general tool for text; I cover it in the guide to add leading zeros to a string in C#.

The same Convert.ToString overload takes base 2 and 8 too, which is how you convert an int to a binary string with leading zeros.

What Happens When You Convert a Negative int to Hex?

Hex formatting shows the bits, not a minus sign. A negative int is stored as two’s complement, so you get the full 32-bit pattern:

int minusOne = -1, minus255 = -255;

Console.WriteLine($"-1 as X8:          {minusOne:X8}");
Console.WriteLine($"-255 as X4:        {minus255:X4}");
Console.WriteLine($"-255 as short X4:  {((short)minus255):X4}");
Console.WriteLine($"-255 as long X:    {((long)minus255):X}");
Console.WriteLine($"Convert.ToString:  {Convert.ToString(minus255, 16)}");
Console.WriteLine($"PadLeft on -255:   {"-FF".PadLeft(6, '0')}");

Output:

-1 as X8:          FFFFFFFF
-255 as X4:        FFFFFF01
-255 as short X4:  FF01
-255 as long X:    FFFFFFFFFFFFFF01
Convert.ToString:  ffffff01
PadLeft on -255:   000-FF
VS Code running HexNegative.cs: C# negative int -1 formatted as hex FFFFFFFF and -255 as FFFFFF01, the two's complement bit pattern
-255 prints 8 digits even with X4, because the width is only a minimum

-1 prints FFFFFFFF, and -255 prints FFFFFF01 even with X4. Cast to short and you get FF01; cast to long and it grows to 16 digits.

So the type decides the width of a negative value. That’s one place where type casting in C# changes the text, not just the value.

Important: don’t build hex by hand with a minus sign. PadLeft on “-FF” gives 000-FF, which no parser will read back.

Convert a Byte Array to Hex with Convert.ToHexString

For bytes (hashes, keys, file headers), skip the loop. Convert.ToHexString pads every byte to two digits for you:

byte[] data = [0x0A, 0xFF, 0x00, 0x7B];

Console.WriteLine(Convert.ToHexString(data));
Console.WriteLine(Convert.ToHexStringLower(data));     // .NET 9+
Console.WriteLine(BitConverter.ToString(data));
Console.WriteLine(string.Concat(data.Select(b => b.ToString("X2"))));

// An int's bytes are little-endian on x64
Console.WriteLine(Convert.ToHexString(BitConverter.GetBytes(255)));

Output:

0AFF007B
0aff007b
0A-FF-00-7B
0AFF007B
FF000000
VS Code running HexBytes.cs: C# Convert.ToHexString prints 0AFF007B, Convert.ToHexStringLower prints 0aff007b and BitConverter.ToString adds dashes
ToHexString keeps the leading zero of every byte

Convert.ToHexString (.NET 5+) prints 0AFF007B, and Convert.ToHexStringLower (.NET 9+) prints 0aff007b. The older BitConverter.ToString adds dashes: 0A-FF-00-7B.

The last line is a classic surprise. BitConverter.GetBytes(255) on x64 is little-endian, so it prints FF000000, not 000000FF. Use ToString("X8") for a single int.

To get bytes from text first, see how to convert a string to a byte array in C#.

How Do You Parse a Hex String Back to int?

Going back, the zeros don’t matter. I parsed the same 00FF a few ways with NumberStyles.HexNumber and Convert.ToInt32:

using System.Globalization;

var inv = CultureInfo.InvariantCulture;

Console.WriteLine(int.Parse("00FF", NumberStyles.HexNumber, inv));
Console.WriteLine(Convert.ToInt32("00ff", 16));
Console.WriteLine(Convert.ToInt32("0x00FF", 16));
Console.WriteLine(int.TryParse("0x00FF", NumberStyles.HexNumber, inv, out _));
Console.WriteLine(int.Parse("FFFFFF01", NumberStyles.HexNumber, inv));
Console.WriteLine(Convert.FromHexString("0AFF007B").Length);

Output:

255
255
255
False
-255
4

int.Parse with NumberStyles.HexNumber and Convert.ToInt32(s, 16) both return 255, upper or lower case. Convert.ToInt32 accepts a 0x prefix, but int.TryParse with HexNumber returns False for it.

FFFFFF01 parses to -255, so the two’s complement round-trips. Convert.FromHexString turns 0AFF007B back into 4 bytes.

Parsing drops the leading zeros, exactly like it does for decimal text in convert string to int with leading zeros. Format again when you print.

A few more conversions you’ll likely need next:

Frequently Asked Questions

How do I convert an int to a 2-digit hex string in C#?

Use value.ToString("X2") or $"{value:X2}". 10 becomes 0A and 255 becomes FF. Values above 255 print more than two digits, because X2 sets a minimum width.

How do I get lowercase hex in C#?

Use a lowercase format specifier: value.ToString("x8") prints 255 as 000000ff. For byte arrays, Convert.ToHexStringLower does the same on .NET 9 and later.

How do I add a 0x prefix to a hex string?

Add it as text: $"0x{value:X4}" prints 0x00FF. The X specifier never adds a prefix, and Convert.ToInt32("0x00FF", 16) can read it back.

Why does a negative number print as FFFFFFFF?

Hex formatting shows the two’s complement bits of the type. An int is 32 bits, so -1 prints as FFFFFFFF; cast to short or byte first for a shorter pattern.

Does ToString(“X”) work with double or decimal?

No. The X specifier is supported only for integral types, and a double throws FormatException. Standard numeric format strings on Microsoft Learn lists which types each specifier supports.