Number bases, byte sizes and the units people argue about

How hex maps onto bits, why a 1 TB drive shows as 931 GB, what 0.1 plus 0.2 actually equals, and how rounding, percentages and locale formatting quietly change a number.

A written number carries two things: a value, and a set of assumptions about how it should be read. 0xFF, 255, 0o377 and 11111111 are one quantity in four notations. A terabyte and a tebibyte differ by ten percent. 1,234 is roughly a thousand in London and roughly one in Berlin. Most numeric bugs come from the assumptions rather than the arithmetic.

Binary, octal, decimal and hexadecimal

Every positional base works the same way. Each place is worth the base times the place to its right, and the digits run from zero to the base minus one. Base 16 needs six extra digits, so it borrows A to F for ten to fifteen.

Hexadecimal is used for bytes and colours because 16 is 2 to the power of 4, so one hex digit is exactly four bits and one byte is exactly two hex digits, with no carrying between them.

0 0000   4 0100   8 1000   C 1100
1 0001   5 0101   9 1001   D 1101
2 0010   6 0110   A 1010   E 1110
3 0011   7 0111   B 1011   F 1111

That table is the whole conversion. To go from hex to binary, replace each digit with its four bits: 0x2F becomes 0010 1111. To go back, group the bits into fours from the right, padding the left with zeros, and read each group off the table.

For decimal, use the place values. 0x2F is 2 x 16 + 15, which is 47. Going the other way, divide by 16 repeatedly and read the remainders bottom to top:

200 / 16 = 12 remainder 8   -> 8
 12 / 16 =  0 remainder 12  -> C
200 decimal = 0xC8

Octal packs three bits per digit, which is why Unix permissions are written as 755: each digit is one rwx triple, 7 being 111 and 5 being 101. A CSS colour such as #FF8800 is three bytes, one per channel, each running 0 to 255, and the three digit shorthand #F80 expands by doubling each digit rather than by padding with zeros. The Number Base Converter covers all four bases at once.

Two's complement, and where ranges come from

A byte holds 256 distinct patterns. Read as unsigned, they are 0 to 255. Read as signed, the top bit marks the negative half, and a negative value is its pattern minus 256. That scheme is two's complement, chosen because ordinary binary addition then works on signed and unsigned values identically.

To negate a number, invert every bit and add one. The range is asymmetric because zero occupies one of the 128 slots in the non-negative half, so a signed byte runs from -128 to 127. Adding one to the top of the range wraps to the bottom:

  0111 1111    127
+ 0000 0001      1
= 1000 0000   -128

Nothing is flagged in most languages; the value simply appears on the other side of the range. The same wrap at 32 bits is the 2038 problem, where a signed 32 bit Unix timestamp runs out at 2,147,483,647 seconds. Unsigned 16 bits gives 0 to 65,535, which is exactly why TCP port numbers stop there, and why the Random Port Generator draws from that range while avoiding the reserved ports below 1024.

Kilobytes, kibibytes and the missing gigabytes

Storage vendors count in powers of ten. Operating systems have historically counted in powers of two and labelled the result with the decimal prefix, which is where the argument starts. The binary prefixes (KiB, MiB, GiB) exist to remove the ambiguity.

Power of tenDecimal valueBinary prefixBinary valueGap
kB, 10^31,000KiB, 2^101,0242.4%
MB, 10^61,000,000MiB, 2^201,048,5764.9%
GB, 10^91,000,000,000GiB, 2^301,073,741,8247.4%
TB, 10^121,000,000,000,000TiB, 2^401,099,511,627,77610.0%
PB, 10^1510^15PiB, 2^501,125,899,906,842,62412.6%

A 1 TB drive holds 10^12 bytes as sold. Divide that by 2^30 and you get 931.32, so Windows reports about 931 GB using the decimal label for a binary quantity. Nothing is missing; the same bytes are being divided by a different number. macOS and most Linux tooling now report decimal GB, which matches the box. Memory sizes are genuinely binary, so 16 GB of RAM really is 16 GiB.

Network speeds are counted in bits, not bytes, and always in decimal. A 100 Mbps line carries 100,000,000 bits per second, which is 12.5 MB per second before overhead. Protocol overhead takes a few percent, so 11 to 12 MB per second is the realistic ceiling. Dividing an advertised megabit figure by eight is the quick check, and the Unit Converter covers the storage units.

Floating point, and why money is not a float

Binary fractions can represent only sums of halves, quarters, eighths and so on. One tenth has no exact binary form, in the way one third has no exact decimal form, so 0.1 stored as an IEEE 754 double is slightly more than a tenth. Add two such approximations and the error surfaces:

0.1 + 0.2 = 0.30000000000000004

A double has 53 bits of significand. That makes every integer up to 2^53 (9,007,199,254,740,992) exactly representable, along with any fraction whose denominator is a power of two. Above 2^53 the gap between representable values becomes 2, so 2^53 + 1 equals 2^53. This is why 64 bit database identifiers sent through JSON should travel as strings: JavaScript numbers are doubles, and a 19 digit ID silently loses its last digits.

For money, store integer minor units (pence, cents) or use a decimal type that works in base ten. Never compare floats with equality; compare the difference against a small tolerance. The Expression Calculator works in doubles too, and inherits the same limits.

Rounding, and rounding twice

Half up rounds a tie away from zero. Half even, also called banker's rounding, sends a tie to the nearest even digit, which removes the upward bias that half up introduces across a long column of figures. It is the IEEE 754 default and the rule in several accounting standards.

ValueHalf upHalf even
0.510
1.522
2.532
3.544
-0.5-10

Round once, from the original value. Rounding 2.4449 to three decimal places gives 2.445, and rounding that to two gives 2.45, while rounding the original straight to two gives 2.44. Chained rounding through intermediate columns is a common source of totals that are off by a penny. A related surprise: 2.675 rounds to 2.67 in most languages, because the stored double is 2.67499999999999982.

Percent and percentage point

A rate moving from 4% to 5% has risen by one percentage point, or by 25% in relative terms. Both are correct, and the unit has to be stated.

Percentage changes do not cancel. A 50% fall followed by a 50% rise leaves 75, not 100, because the second percentage is taken from a smaller base. Recovering a fall of p requires a rise of p / (1 - p), so a 50% fall needs 100%, and an 80% fall needs 400%. Successive changes multiply, making a geometric sequence rather than a sum, which is the growth the Number Sequence tool models.

The change from an old value to a new one is (new - old) / old x 100, which is what the Percentage Calculator reports as a difference. Removing 20% VAT means dividing by 1.2, not subtracting 20%. Averaging percentages across groups of different sizes gives the wrong overall figure unless the average is weighted, and Sum and Average showing a median next to the mean makes the skew visible.

Numbers formatted for a locale

Formatting is a display decision, and it is not reversible without knowing the locale. 1.234 is one and a bit in the UK and one thousand in Germany. 1,234 is the reverse. Some locales group with a space or an apostrophe instead.

Locale1234567.89
en-GB, en-US1,234,567.89
de-DE1.234.567,89
fr-FR1 234 567,89
de-CH1'234'567.89
en-IN12,34,567.89

Indian grouping puts the first separator after three digits and every two digits after that, giving lakh and crore rather than thousands and millions. Parsing a formatted string back into a number is lossy for exactly this reason, so store and transmit values unformatted with a dot as the decimal separator, and format only at the point of display. Where a number has to be unambiguous to a reader, spelling it out with Number to Words is the usual approach on cheques and in contracts. Roman numerals sit outside all of this: with no place value and no zero, the Roman Numeral Converter is handling a tally rather than a base.