Decimal to hex in C#
using System.Numerics;
Console.WriteLine(255.ToString("X")); // FF
Console.WriteLine(255.ToString("x4")); // 00ff
Console.WriteLine((-1).ToString("X")); // FFFFFFFF
Console.WriteLine(((short)-1).ToString("X")); // FFFF
Console.WriteLine(Convert.ToString(-1, 16)); // ffffffff
Console.WriteLine(Convert.ToHexString(BitConverter.GetBytes(255))); // FF000000
Console.WriteLine(new BigInteger(255).ToString("X")); // 0FF
Console.WriteLine(new BigInteger(-1).ToString("X")); // F
value.ToString("X")writes uppercase hex,"x"lowercase. A number after it pads with zeros to at least that many digits and never cuts digits off:255.ToString("X8")is 000000FF. For negative values it writes the two's complement bits of the type, so the width depends on the type:(-1).ToString("X")is FFFFFFFF for anintand FFFF for ashort.Convert.ToString(value, 16)(or 2 or 8) writes lowercase and also uses two's complement for negatives. It only has overloads forbyte,short,intandlong: ansbytegoes to theshortone, soConvert.ToString((sbyte)-1, 16)is ffff, and aulongdoes not compile.Convert.ToHexString(BitConverter.GetBytes(value))gives the bytes in the machine's order, little-endian on x64 and Arm64: 255 is FF000000. UseToString("X8")for the number.- BigInteger adds a leading 0 when the first hex digit is 8 to F, so the text parses back to a positive number:
new BigInteger(255).ToString("X")is 0FF, and -1 is F.
Two's complement: why FFFFFFFF is -1
C# stores sbyte, short, int and long in two's complement: the top bit counts as minus 2n-1 instead of plus. So the 32 bits FFFFFFFF are -1 as an int and 4294967295 as a uint, and 80000000 is int.MinValue. To negate a value, invert the bits and add 1: 1 is 00000001, -1 is FFFFFFFF, -255 is FFFFFF01.
A cast between types of the same width keeps the bits and changes their meaning; a cast to a narrower type keeps the low bits. Constants need unchecked: (int)0xFFFFFFFF does not compile (error CS0221), unchecked((int)0xFFFFFFFF) is -1. Pick a width above to see both readings of the same bits.
C# integer literals and their type
An integer literal without a suffix gets the first of int, uint, long, ulong that can hold it. With u it is uint or ulong, with L long or ulong, and with UL always ulong. Hex and binary follow the same rule, which is why 0xFFFFFFFF is a uint and int x = 0xFFFFFFFF; does not compile:
Console.WriteLine((0x7FFFFFFF).GetType().Name); // Int32
Console.WriteLine((0xFFFFFFFF).GetType().Name); // UInt32
Console.WriteLine((0x1_0000_0000).GetType().Name); // Int64
Console.WriteLine((0xFFFF_FFFF_FFFF_FFFF).GetType().Name); // UInt64
Console.WriteLine((0xFFu).GetType().Name); // UInt32
Console.WriteLine((0xFFL).GetType().Name); // Int64
Console.WriteLine((-2147483648).GetType().Name); // Int32
Console.WriteLine((-2147483649).GetType().Name); // Int64
Console.WriteLine(010); // 10
- Prefixes:
0xor0Xfor hex,0bor0Bfor binary (C# 7). C# has no octal literal:010is ten. - Digit separators:
_can go between any digits, and more than one in a row:1_000_000,0b1010_1010. Right after0xor0bis fine too (0x_FF, C# 7.2), but not at the end:0xFF_is error CS1013, "Invalid number". - Suffixes:
u,l,ulorluin any case. A lowercaselfirst draws warning CS0078 because it looks like a 1; writeL. - Negative literals: the minus is an operator applied to the literal, so
-2147483648is an int only thanks to a special rule,-2147483649is a long, and-18446744073709551615is error CS0023, sinceulonghas no minus. Pastulong.MaxValuea literal is error CS1021, "Integral constant is too large"; useBigInteger.Parse.
FAQ
How do I convert an int to hex in C#?
value.ToString("X") for uppercase, "x" for lowercase, "X8" to pad to 8 digits. $"{value:X8}" does the same in an interpolated string.
How do I convert a negative number to hex?
Hex of a negative number is its two's complement bits at a fixed width. In C#, (-1).ToString("X") is FFFFFFFF because -1 is an int; cast to short or sbyte first for FFFF or FF.
Why does my hex have a leading 0 with BigInteger?
BigInteger.ToString("X") keeps the sign in the first digit. 255 would be FF, which reads back as -1, so it writes 0FF.
Why does BitConverter turn 255 into FF000000?
BitConverter returns the bytes in the machine's order, and x64 and Arm64 are little-endian, so the lowest byte comes first. Format the number instead: 255.ToString("X8").