§ 01 · Bytes & numbers
Hexadecimal
Counting in sixteens, two characters per byte
On this page 7 sections
HexadecimalHexadecimal: A way of writing numbers with sixteen digits, 0–9 then a–f. One byte is always exactly two hex characters, which is why keys and hashes are shown in hex. (“hex”) is a way of writing numbers with sixteen digits instead of ten.
You’ll see it everywhere in Pearl. Every key, hash, signature and raw transaction on this site is written in hex. That’s not because the numbers are special. It’s because hex is the tidiest way to write down bytes.
Try it
Type a number in any box. Watch it change between decimal, hex and binary.
What are the sixteen digits?
Normal counting (decimal) has ten digits, 0 to 9. When you run out, you carry one into the next column: 9, then 10.
Hex has six more digits before it carries. It borrows letters for them:
| Decimal | 0–9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
|---|---|---|---|---|---|---|---|---|
| Hex | 0–9 | a | b | c | d | e | f | 10 |
So in hex, “10” means sixteen, not ten. To avoid confusion, hex numbers are often written with a 0x in front: 0x10 is sixteen.
Capital and small letters mean the same thing. Pearl’s software prints small letters.
Why does every byte take two characters?
Because sixteen is a power of two.
One hex digit has 16 possible values, which is exactly what four bitsBit: The smallest piece of digital information: a 0 or a 1. Eight bits make a byte. can hold (2 × 2 × 2 × 2 = 16). Half a byte, four bits, is sometimes called a nibbleNibble: Half a byte: four bits, which is exactly one hexadecimal digit (0 to f).. Two nibbles make a byte, so one byte is always exactly two hex characters, from 00 to ff.
This is the whole reason hex is used. Binary is too long to read (a hash is 256 ones and zeros). Decimal hides where one byte ends and the next begins. Hex is short and keeps the bytes visible: every pair of characters is one byte.
How do you convert hex to decimal?
Each column is worth sixteen times the one to its right, just as each decimal column is worth ten times more.
For a longer number, keep going with 256, 4,096, 65,536 and so on. Pearl’s coin type, the number in every wallet’s derivation pathDerivation path: The route through a wallet's key tree to one key, written like m/86'/808276'/0'/0/0 (Pearl's first receive address). 808276 comes from the letters P, R, L (codes 80, 82, 76)., is 808,276. In hex it is 0xc5554:
Going the other way: decimal to hex
Divide by 16 and write down the remainder. Repeat with the quotient until it reaches zero. The remainders, read from last to first, are the hex digits.
| Step | Divide | Quotient | Remainder |
|---|---|---|---|
| 1 | 194 ÷ 16 | 12 | 2 |
| 2 | 12 ÷ 16 | 0 | 12 = c |
Reading the remainders upwards gives c2. The same method turns block height 130,825 into 0x1ff09.
Where do you see hex in Pearl?
Almost everywhere data is shown rather than spoken:
- Hashes. A block hash like
3fc851763893a0135ea756d5dcbe355f375e613957930e7f401fafc8ee5533aeis 32 bytes, so 64 characters. - Raw data. A 108-byte Pearl header is 216 hex characters. A raw transaction is its bytes in hex, start to finish.
- Keys and signatures. A private key is 64 characters, an x-only public key 64, a Schnorr signature 128.
- Constants in the code. Pearl’s difficulty-1 bits are
0x1b00ffff, the XMSS opcode is0xde, and the mainnet network magic is0x50524C4D, which spells “PRLM” in ASCII.
Make a guess
Block 130,825's coinbase starts its script with the bytes 03 09 ff 01. The last three bytes hold the block height. 0x1ff09 is 130,825, but the bytes say 09 ff 01. Why are they in that order?
Think of your answer first, then check it.
Answer The bytes are little-endian: smallest byte first.
Read backwards, 09 ff 01 becomes 01 ff 09, which is 0x1ff09 = 130,825. Hex shows you the bytes faithfully, in
the order they are stored. Whether you read them forwards or backwards is a separate question, answered on the
byte order page.
How do you find one byte in a long hex string?
Count in pairs. Byte number n (counting from 0) starts at character 2n, and every field is twice as many characters as it is bytes.
Take block 130,825’s header, 216 characters long. Its bits field is bytes 72 to 75, so it sits at characters 144 to 151: ebd06a17. Its Merkle root is bytes 36 to 67, characters 72 to 135.
Pearl’s code, explorers and this site always give offsets in bytes, not characters. When a page says “offset 76”, double it to find the spot in the hex.
| Field | Bytes | Value |
|---|---|---|
| Version header.version | 4 @0 | 0x20000000 int32 LE. BIP-9 style 0x20000000 since block 1; no consensus meaning in Pearl. |
| Previous block header.prevBlock | 32 @4 | abc77adee0d6eda61720687d5642efbe4a6a7d9853eee1dad0c559dc431e22d3 Hash of the parent block, stored byte-reversed; shown in display order. |
| Merkle root header.merkleRoot | 32 @36 | ab5f7fee74ac4fc304186ab08b556fd03e408576753b1f9ae4574d48251d052a Root of the SHA256d Merkle tree of this block’s txids, stored byte-reversed. |
| Time header.timestamp | 4 @68 | 1792512899 (2026-10-20 16:14:59 UTC) uint32 LE Unix seconds. Must be at least 1 s after the parent and at most 5 min in the future. |
| Bits header.bits | 4 @72 | 0x176ad0eb Compact target. Difficulty 40208379.91968057. The certificate's jackpot hash (not this header's hash) must meet it. |
| Proof commitment header.proofCommitment | 32 @76 | 86bad84eb6090f941048ea203ac102cf00db145383052c866ccae4e012e36dce SHA256d(LE32(certificate version) ‖ public data). Replaces Bitcoin’s nonce: Pearl headers have no nonce. |
Is hex a different kind of number?
No. Hex is only a way of writing a number, like Roman numerals. 0xc2, 194 and 11000010 are the same value, stored as the same eight bits.
That matters when you hash things. Hashing the text “c2” (two characters, two bytes) is not the same as hashing the byte 0xc2 (one byte). Tools on this site always say which one they mean.
Code: hex in Pearl's source and in JavaScript
pearld prints a hash by reversing its 32 bytes and hex-encoding them, in
Hash.String() (node/chaincfg/chainhash/hash.go:73-80 at v1.5.2, on GitHub):
// String returns the Hash as the hexadecimal string of the byte-reversed
// hash.
func (hash Hash) String() string {
for i := 0; i < HashSize/2; i++ {
hash[i], hash[HashSize-1-i] = hash[HashSize-1-i], hash[i]
}
return hex.EncodeToString(hash[:])
} In a browser:
(194).toString(16); // 'c2'
parseInt('c5554', 16); // 808276
const hex = (bytes) => [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('');