Binary Converter
- Binary
10 1010- Octal
52- Decimal
42- Hexadecimal
2a
Two's complement
- 8-bit
0010 1010- 16-bit
0000 0000 0010 1010- 32-bit
0000 0000 0000 0000 0000 0000 0010 1010- 64-bit
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010 1010
Nothing you enter here is stored, logged or shared.
Convert a number between binary, octal, decimal and hexadecimal, or convert text to binary and back. Values stay exact at any size, including well beyond 64 bits.
How to use
- Pick the base you are typing in and enter your value.
- Every other base is shown at once, with two's complement below.
- Switch to Text mode to convert words to binary and back.
Why the arithmetic is exact
Every conversion here runs on BigInt, which has no size limit and no rounding. That sounds like an implementation footnote and is actually the main thing separating this from most converters on the web.
JavaScript numbers are IEEE 754 doubles, and they represent integers exactly only up to 9,007,199,254,740,991, which is 2^53 minus one. A converter written the obvious way is correct below that and quietly wrong above it. The failure mode is the bad kind: no error, no warning, just an answer of the right length and shape that happens not to be the right answer.
The threshold is low enough to hit constantly. A full 64-bit value is far past it. So are database IDs from a snowflake generator, nanosecond timestamps, hardware register dumps, hashes, and anything read out of a 64-bit memory address. If you are converting a number large enough to be interesting, an ordinary converter is exactly where it stops being reliable.
Two's complement
Below the four bases the value is shown as it would be stored in a signed 8, 16, 32 or 64-bit integer. This is not the same as writing a minus sign in front of the binary digits, and the difference is what trips people up when reading memory.
In two's complement a negative number is formed by inverting all the bits of its magnitude and adding one. In eight bits, -1 is 11111111 and -128 is 10000000. There is no separate sign bit to strip off, no negative zero, and addition works without special-casing the sign, which is why every mainstream processor stores integers this way.
It also explains the asymmetric range. Eight bits hold -128 to 127 rather than -128 to 128, because zero occupies one of the slots on the positive side. A width the value does not fit in says so instead of truncating it silently, since a truncated result looks perfectly valid and is not.
Reading the output
Long values are grouped so they can be read at a glance: binary in bytes of eight, hexadecimal in pairs, decimal in thousands. The grouping is display only and is stripped from anything you copy.
Input is deliberately forgiving in the same direction. The 0x, 0b and 0o prefixes are accepted and ignored, as are underscores, spaces and commas used as separators, so a literal pasted straight out of source code or a hex dump works without cleaning it up first.
Text to binary
Text mode encodes the text as UTF-8 and then writes each byte as eight bits. Going the other way, the bits are read back as bytes and decoded as UTF-8.
The alternative - taking each character's code unit directly - is what most text-to-binary tools do, and it breaks in two places. Anything above U+00FF produces more than eight bits and no longer lines up with bytes at all. Worse, characters outside the Basic Multilingual Plane, which includes most emoji, are stored in JavaScript as two surrogate code units; treated individually they become two meaningless halves that decode to nothing on the other side. Going through UTF-8 avoids both problems, which is why an emoji here round-trips and elsewhere often does not.
One number, four ways of writing it
Take 2,025. In binary it is 11111101001, in octal 3751, in hexadecimal 7E9. Nothing about the quantity changed; only the base used to write it down. The digits of any base are place values: 7E9 is 7x256 plus 14x16 plus 9, and 11111101001 is the sum of the powers of two whose bit is set.
Hexadecimal earns its place because sixteen is two to the fourth, so one hex digit is exactly four bits and the mapping is memorisable: F is 1111, 8 is 1000, A is 1010. That is why memory dumps, colour values, hashes and MAC addresses are written in hex rather than decimal - you can read the bits straight off the digits without doing any arithmetic.
Octal survives mostly in one place, Unix file permissions, for the same reason: three bits per digit maps perfectly onto read, write and execute, so 755 is rwxr-xr-x and nothing has to be looked up.
Bitwise operations, and what masks are for
Once a value is in binary the bitwise operators are obvious. AND keeps only the bits set in both operands, which is how you test a flag: value AND 0b0100 is non-zero exactly when the third bit is set. OR sets bits, XOR flips them, NOT inverts everything, and shifting left or right multiplies or divides by powers of two.
This is why flags are stored as single bits rather than separate booleans. A permissions field, a set of feature toggles, or the options on a network packet can live in one integer, and checking one is a single instruction. The constant you AND with is called a mask, and writing it in binary or hex makes its purpose visible in a way the decimal equivalent never does.
Shifts have one trap worth knowing: in most languages a right shift on a signed value keeps the sign bit, so -8 shifted right is -4 rather than a large positive number. Languages that offer both spell the other one differently, as in JavaScript's >>> operator.
How a number sits in memory
A value written as 7E9 does not tell you how it is stored. Width matters: the same number occupies one, two, four or eight bytes depending on the type, and a value too large for its width is truncated or wraps around rather than growing.
Byte order matters too. Little-endian machines, which is nearly everything you will meet, store the least significant byte first, so the 32-bit value 0x000007E9 appears in a hex dump as E9 07 00 00. Big-endian order is the reverse and is what network protocols specify, which is why it is called network byte order and why conversion functions exist at every socket boundary. Reading a dump without knowing which one you are looking at produces confident nonsense.
Unsigned and signed types of the same width hold the same bits and interpret them differently: 0xFF is 255 as an unsigned byte and -1 as a signed one. Nothing in the bytes says which is correct; only the type does.
Floating point is a different system entirely
Everything above concerns integers. A double is stored as a sign bit, an 11-bit exponent and a 52-bit fraction, which means it represents a number as a fraction multiplied by a power of two. Values that are not exactly representable that way get rounded to the nearest one that is.
That is the whole explanation for the famous result where 0.1 plus 0.2 does not equal 0.3. One tenth in binary is a repeating fraction, exactly as one third is in decimal, so it is stored slightly off, and two slightly-off values add to something slightly off. The arithmetic is correct; the expectation that decimal fractions survive binary storage is what is wrong.
The practical rules follow: never store money in a float, use integer cents or a decimal type; compare floats with a tolerance rather than equality; and be aware that integers above 2^53 lose precision in a double, which is why this converter uses arbitrary-precision arithmetic instead.
Where you meet these bases in practice
Hexadecimal turns up wherever bytes are shown to a person. A colour in CSS is three bytes of hex; a MAC address is six; a SHA-256 digest is thirty-two, written as sixty-four characters. Memory addresses, error codes from operating systems and hardware registers are all hex because the bit boundaries line up with the digits.
Binary appears where individual bits carry meaning: network masks, hardware registers, permission bits, feature flags, and the encoding tables of formats like QR codes and UTF-8. Reading UTF-8 is a good example of why the base matters - a byte beginning 0 is ASCII, one beginning 110 starts a two-byte sequence, and one beginning 10 is a continuation byte. That structure is invisible in decimal and obvious in binary.
Octal has largely retreated to file permissions and a few legacy formats, but it retains one trap worth remembering: in C, JavaScript before ES5, and several configuration formats, a number written with a leading zero is octal, so 0755 is not seven hundred and fifty-five. Modern JavaScript uses the explicit 0o prefix, and YAML 1.2 dropped the leading-zero rule entirely, which is its own source of surprise when a file mode is read as a decimal number.
Frequently asked questions
- Does this stay accurate with very large numbers?
- Yes. All arithmetic uses BigInt, which is exact at any size. Converters built on ordinary JavaScript numbers lose precision above 2^53, roughly 9 quadrillion, and then return answers that look plausible but are wrong. A full 64-bit value already exceeds that limit, so this matters more often than it sounds.
- What is two's complement and why show it?
- It is how signed integers are actually stored in memory. In 8-bit two's complement, -1 is 11111111 rather than a sign bit plus 1. Seeing the stored form is what you need when reading a register dump or debugging a protocol. Widths where the value does not fit say so rather than silently truncating.
- Can I paste values with 0x or 0b prefixes?
- Yes. Those prefixes are accepted and ignored, as are underscores, spaces and commas used for grouping, so you can paste straight from source code.
- How is text converted to binary?
- The text is encoded as UTF-8 first, then each byte is written as eight bits. That means Japanese, Chinese, Korean and emoji round-trip correctly. Tools that read character codes directly produce nonsense for anything outside ASCII, and a single emoji becomes two broken halves.
Last updated:
Include the steps, expected result, and actual result. Remove secrets and personal data.
