Try the Number Base Converter

Why Your RTC Chip Returns 89 When the Time Is 59 — BCD, Gray Code, and Other Number Encodings in Modern Hardware

Binary-Coded Decimal (BCD) encodes each decimal digit as a 4-bit group — which is why real-time clock chips return 0x59 to mean 59 seconds (not 89), and why hardware with BCD limits constrains values to 0-99 rather than 0-255. Here's BCD in RTC hardware and financial calculations, Gray code's one-bit-change property that prevents rotary encoder errors, and why TCP checksums use one's complement arithmetic.

July 19, 2026 6 min read
Share: Facebook WhatsApp LinkedIn Email
Why Your RTC Chip Returns 89 When the Time Is 59 — BCD, Gray Code, and Other Number Encodings in Modern Hardware

Binary-coded decimal (BCD) — a system where each decimal digit is represented by four binary bits — was the dominant method for encoding numbers in early computing, persists in modern hardware timekeeping chips, and explains why some numbers in embedded systems look oddly constrained to values like 9, 19, 29 rather than values like 15, 31, or 255

The previous articles on this site covered binary/octal/decimal/hex basics, negative numbers and overflow, bit manipulation and bitmasks, IEEE 754 floating point, three everyday hex formats, and reading hex for debugging. This article addresses number encoding systems beyond standard binary — specifically BCD, Gray code, and one's complement, the historical reasons they exist, and the specific modern contexts where they still appear.


Binary-Coded Decimal: why "09" in binary isn't just 9

Standard binary encodes numbers by their mathematical value: 9 = 1001₂, 10 = 1010₂, 15 = 1111₂, 16 = 10000₂.

Binary-Coded Decimal (BCD) encodes each decimal digit separately:

  • Each decimal digit (0-9) is encoded as a 4-bit group
  • 9 in BCD = 1001 (same as binary 9)
  • 10 in BCD = 0001 0000 (one, zero — two separate 4-bit groups)
  • 99 in BCD = 1001 1001 (nine, nine)
  • 256 in BCD = 0010 0101 0110 (two, five, six — three 4-bit groups)

Why the "odd" constraint: BCD groups can only hold values 0-9. The patterns 1010 through 1111 (10-15 in binary) are "illegal" BCD — they never appear. This is why devices using BCD have limits that look decimal rather than binary: a BCD byte (two 4-bit digits) holds 00-99, not 0-255.


Where BCD still appears in modern hardware

Real-Time Clock (RTC) chips — the hardware clocks that keep time in computers, microcontrollers, embedded systems, and IoT devices — almost universally use BCD encoding:

The DS1307 and DS3231 (popular RTC chips): store seconds, minutes, hours, day, date, month, and year all in BCD format. When you read the "seconds" register, you get a BCD-encoded value:

Register value: 0x59 (binary: 0101 1001)
BCD interpretation: 5 (upper nibble), 9 (lower nibble) → 59 seconds
Binary value: 89 (decimal) — wrong if interpreted as standard binary

The conversion requirement: software reading from an RTC chip must decode BCD:

// BCD to decimal conversion
uint8_t bcd_to_dec(uint8_t bcd) {
    return (bcd / 16 * 10) + (bcd % 16);
}
// or equivalently:
uint8_t bcd_to_dec(uint8_t bcd) {
    return ((bcd >> 4) * 10) + (bcd & 0x0F);
}

Why RTCs use BCD: RTC chips were designed when displaying time on 7-segment displays was the primary output. 7-segment display controllers natively decode BCD to segment patterns — each digit can drive one display digit directly. The entire pipeline from RTC chip to display required no binary-to-decimal conversion, simplifying circuit design in an era when every component counted.


BCD in financial calculations: avoiding floating-point errors

Financial calculations — currency amounts, tax calculations, accounting — can't afford the floating-point rounding errors discussed in the IEEE 754 article. One approach is BCD arithmetic (also called packed decimal):

IBM's EBCDIC and packed decimal: IBM mainframes used BCD (called "packed decimal") for financial arithmetic for decades. The System/360 instruction set has dedicated BCD arithmetic instructions (Pack, Unpack, Add Packed, Multiply Packed) that operate on packed decimal representations.

Modern language equivalents:

  • Java: BigDecimal class
  • Python: decimal module with Decimal type
  • C#: decimal type (128-bit, not BCD but avoids floating-point issues)

The BCD approach in modern embedded finance: some embedded systems (ATMs, point-of-sale terminals, card readers) still use BCD arithmetic for currency amounts to guarantee exact decimal representation.


Gray code: binary that changes one bit at a time

Gray code (also called "reflected binary code") is a binary encoding where consecutive integers differ by exactly one bit:

Decimal Binary Gray code
0 000 000
1 001 001
2 010 011
3 011 010
4 100 110
5 101 111
6 110 101
7 111 100

Why one-bit-change matters — the rotary encoder: a rotary encoder measures angular position. If the encoder uses standard binary and is positioned exactly at the transition from 3 (011) to 4 (100), all three bits change simultaneously. During the physical transition, the bits may not change simultaneously — an intermediate reading of 111 (7) or 101 (5) could briefly occur, producing a large erroneous position reading.

With Gray code: 3 is 010, 4 is 110 — only the most significant bit changes. No intermediate invalid states are possible during the transition. This is why virtually all rotary encoders and angular position sensors use Gray code.

Conversion between Gray code and binary:

// Binary to Gray code
unsigned gray = binary ^ (binary >> 1);

// Gray code to binary
unsigned binary = gray;
while (binary >>= 1) gray ^= binary;
// (iterative version; also done with leading bit propagation)

One's complement: the predecessor to two's complement

One's complement was an earlier method for representing negative numbers in binary:

  • Positive numbers: standard binary
  • Negative numbers: invert all bits (NOT operation)
+5 in 8-bit one's complement: 0000 0101
−5 in 8-bit one's complement: 1111 1010 (all bits inverted)

The problem with one's complement: it has two representations of zero:

  • +0 = 0000 0000
  • −0 = 1111 1111

Having two zeros complicates arithmetic and comparison. Two's complement (the modern standard) solves this by representing negative numbers as NOT(n) + 1, producing a single zero representation and simpler arithmetic hardware.

Where one's complement still appears: one's complement checksums are used in the TCP and UDP header checksum algorithms (RFC 793 and RFC 768). The checksum is computed by summing 16-bit words in one's complement arithmetic (with carry wrap-around) and taking the one's complement of the result. This was chosen because the one's complement sum is self-inverse — verifying a checksum requires only summing all words and checking for all-ones result.


How to use the Number Base Converter on sadiqbd.com

  1. For BCD debugging: when reading values from RTC chips, sensors, or legacy hardware that uses BCD, convert each 4-bit nibble independently — don't treat the whole byte as a standard hex value
  2. For Gray code: the converter handles binary ↔ decimal ↔ hex conversion; Gray code conversion requires the XOR-based formula shown above before or after using the tool
  3. For embedded system work: when you see a hex value that seems wrong (like 0x59 meaning 59 but reading as 89 in decimal), BCD encoding is the most common culprit — the tool shows the decimal value of the hex, which lets you spot the BCD interpretation difference

Frequently Asked Questions

Why does Python's datetime module sometimes produce values that look like they might be BCD-related when working with timestamps? Python's datetime module uses standard binary internally — the BCD connection comes from the hardware level, not Python. When you read from an RTC chip using Python (via I2C with smbus or similar), the raw bytes are BCD-encoded. If you forget to convert BCD to decimal before creating a datetime object, you'll get the wrong time — for example, 0x59 (89 decimal) for seconds instead of 59. The issue is at the hardware read layer, not in Python's datetime. The fix: apply bcd_to_dec() conversion to each byte before constructing the datetime object. Libraries like adafruit-circuitpython-ds3231 handle this conversion automatically; lower-level I2C reads require manual BCD conversion.

Is the Number Base Converter free? Yes — completely free, no sign-up required.

Try the Number Base Converter free at sadiqbd.com — convert between binary, octal, decimal, and hexadecimal instantly.

Share: Facebook WhatsApp LinkedIn Email

Number Base Converter

Free, instant results — no sign-up required.

Open Number Base Converter →
Similar Tools
HTML Entities Bcrypt Generator Random String Generator Password Generator JSON Formatter JSON Diff REST API Checker Color Converter
Bit Manipulation and Bitmasks: From Unix Permissions to Feature Flags
Developer
Bit Manipulation and Bitmasks: From Unix Permissions to Feature Flags
IEEE 754 Floating-Point: Why 0.1 + 0.2 ≠ 0.3 and What to Use for Financial Calculations
Developer
IEEE 754 Floating-Point: Why 0.1 + 0.2 ≠ 0.3 and What to Use for Financial Calculations
#FF5733, 00:1A:2B:3C:4D:5E, and IPv6: Three Everyday Hex Formats, Three Different Reasons for Hex
Developer
#FF5733, 00:1A:2B:3C:4D:5E, and IPv6: Three Everyday Hex Formats, Three Different Reasons for Hex
How to Read Hex Like a Debugger — Magic Bytes, Memory Dumps, and Why Byte Order Matters
Developer
How to Read Hex Like a Debugger — Magic Bytes, Memory Dumps, and Why Byte Order Matters