Sunday, November 1, 2026||
MultiKhabarMultiKhabar

Why Your Hex Converter Lies to You: Two's Complement, the 53-Bit Wall, and Radix Magic

HA
HackerNoon
By Elias Miller
Share:WhatsAppFacebookX (Twitter)Telegram
Why Your Hex Converter Lies to You: Two's Complement, the 53-Bit Wall, and Radix Magic
Photo / Coverage: HackerNoonCredit: Elias Miller
EXECUTIVE SUMMARY

An in-depth exploration of two's complement, sign extension traps, IEEE-754 precision limits in JavaScript, and why Radix 36 is the superior choice for URL-safe

Key Takeaways

  • Why Your Hex Converter Lies to You: Two's Complement, the 53-Bit Wall, and Radix Magic.
  • Widen it to a 32-bit register, and it suddenly balloons into 0xFFFFFFFF.Where did the minus sign go?
  • Why didn't it output -0x01?

Every programmer eventually encounters that moment of quiet confusion during a late-night debugging session: you cast a signed 8-bit -1 to hexadecimal, expecting something clean and human-readable, only for the debugger to spit back 0xFF. Widen it to a 32-bit register, and it suddenly balloons into 0xFFFFFFFF.Where did the minus sign go? Why didn't it output -0x01? And why does running 0x20000000000001 through your standard JavaScript parser silently yield the wrong integer altogether?Number base conversion is often treated as a trivial Computer Science 101 exercise—something solved with a simple parseInt(val, 16) or a textbook division-by-radix loop. But beneath high-level abstractions, radix shifts are inextricably linked to hardware registers, two’s complement mathematics, IEEE-754 rounding traps, and data-encoding strategies.Here is what is actually happening under the hood when bits change their radix, and why understanding these low-level mechanics prevents catastrophic bugs in production.1. The Illusion of the Minus Sign: How Two's Complement WorksHardware processors don't have dedicated silicon cells reserved for storing an aesthetic - glyph. In CPU registers, negative numbers are encoded via Two’s Complement.In an NNN-bit register, the most significant bit (MSB) acts as a negative weight:Value=−bN−12N−1+∑i=0N−2bi2i\text{Value} = -b_{N-1} 2^{N-1} + \sum_{i=0}^{N-2} b_i 2^iValue=−bN−1​2N−1+i=0∑N−2​bi​2iWhen you assign -1 to an 8-bit signed integer (int8_t):Invert all bits of positive 1 (0000 0001 →\rightarrow→ 1111 1110).Add 1 (1111 1110 + 0000 0001 = 1111 1111).In hexadecimal, each character represents an exact 4-bit nibble. Thus, 1111 1111 maps directly to 0xFF.c #include <stdio.h> #include <stdint.h> int main() { int8_t small_neg = -1; int32_t wide_neg = small_neg; // Sign extension occurs here printf("8-bit hex: 0x%02X\n", (uint8_t)small_neg); // 0xFF printf("32-bit hex: 0x%08X\n", (uint32_t)wide_neg); // 0xFFFFFFFF return 0; } The Sign Extension TrapNotice what happened when widening int8_t to int32_t: the single byte 0xFF became 0xFFFFFFFF.If the hardware merely padded the higher 24 bits with zeros (0x000000FF), the value would evaluate to +255, silently mutating your application state. To preserve the mathematical value of -1, the CPU performs Sign Extension: it duplicates the MSB (which is 1) across all newly allocated upper bits.Pro-Tip for Packet Inspection: When inspecting raw network packets or memory dumps, you can instantly determine if a hexadecimal value represents a negative signed integer without converting to decimal: look at the leading character.Because 8 through F have their highest bit set to 1:0x8... through 0xF... →\rightarrow→ Negative (in signed contexts)0x0... through 0x7... →\rightarrow→ Zero or Positive2. Arithmetic Shift (>>) vs. Logical Shift (>>>): A Subtle Production HazardUnderstanding two’s complement makes the difference between bitwise shift operators crystal clear:Arithmetic Right Shift (>>): Preserves the sign bit. Vacated positions on the left are backfilled with whatever the MSB was.Logical Right Shift (>>>): Agnostic to sign. It unconditionally backfills vacated positions with zeros.typescript const value = -42; // Binary: 11111111 11111111 11111111 11010110 (in 32-bit) console.log(value >> 1); // Output: -21 (MSB remains 1; arithmetic integrity preserved) console.log(value >>> 1); // Output: 2147483627 (MSB replaced with 0; immediately converts to large unsigned integer) In telemetry, hashing routines, and protocol deserialization, accidentally using >>> instead of >> (or vice versa) produces bugs that unit tests with small positive integers never catch.3. The 53-Bit Wall: Why JavaScript Silently Corrupts Large RadixesConsider this seemingly benign conversion snippet:javascript const highSeqId = "9007199254740993"; // 2^53 + 1 console.log(Number(highSeqId).toString(16)); // Expected: "20000000000001" // Actual: "20000000000000" (Precision collapsed!) Standard JavaScript Number types conform to the IEEE-754 double-precision floating-point specification. Double floats allocate:1 sign bit11 exponent bits52 explicit fraction bits (providing 53 bits of precision with the implicit leading 1)Any integer larger than Number.MAX_SAFE_INTEGER (253−12^{53} - 1253−1, or 9,007,199,254,740,991) cannot be uniquely represented. When you convert an arbitrary-large hexadecimal hash or a 64-bit Snowflake ID via standard floating-point numbers, the least significant bits get silently rounded to the nearest representable floating-point neighbor.The Fix: BigInt and LimbsArbitrary-precision arithmetic operates outside the Floating-Point Unit (FPU). Instead of relying on a single hardware register, runtimes allocate heap arrays of 64-bit integer chunks (often called machine limbs):javascript // Lossless 64-bit / 128-bit Radix Transformations const safeVal = BigInt("0x20000000000001"); console.log(safeVal.toString(10)); // "9007199254740993" (Exact) console.log(safeVal.toString(36)); // "2000000000001" 4. Radix 36: The Workhorse of URL Slugs and Distributed IDsWhile base-2 (binary), base-8 (octal), and base-16 (hexadecimal) map directly into CPU word architectures because they are powers of two (21,23,242^1, 2^3, 2^421,23,24), higher non-power-of-two radices serve specialized architectural purposes.Chief among them is Radix 36 (0–9, a–z).MetricBase-10Base-16 (Hex)Base-36Base-64Character Set0–9 (10)0–9, a–f (16)0–9, a–z (36)A–Z, a–z, 0–9, +, / (64)Case Sensitive?NoNoNoYesURL-Safe natively?YesYesYesNo (+, /, = require percent-encoding)Chars for 2642^{64}264~20161311While Base-64 yields the highest raw density, its reliance on uppercase versus lowercase characters makes it vulnerable to collisions on case-insensitive filesystems and legacy databases. Furthermore, standard Base-64 requires URL-safe variations (RFC 4648) to avoid collision with URI reserved characters.Radix 36 hits the sweet spot: it is totally case-insensitive, yields compact strings, and requires zero URL encoding.python def to_base36(num: int) -> str: alphabet = "0123456789abcdefghijklmnopqrstuvwxyz" if num == 0: return "0" arr = [] while num: num, rem = divmod(num, 36) arr.append(alphabet[rem]) return "".join(reversed(arr)) print(to_base36(1685894123512)) # "hicw5f3g" (Clean slug ready for routing) 5. Visualizing & Validating Complex Edge CasesWhen designing custom bitmasks, verifying register overflows, or calculating variable-width two's complement strings, hand-calculating bit shifts invites human error.If you are debugging packet headers or writing firmware-level parsers, you can cross-reference your math against this interactive Number Base Converter sandbox, which calculates signed/unsigned representations, arbitrary-width sign extensions, and custom radices from 2 to 36 without floating-point precision drops.Summary Checklist for Systems DevelopersCheck your signedness: 0xFF is 255 in an unsigned 8-bit byte, but -1 in a signed two's complement register.Be cautious with bitwise shifts in JavaScript: JS converts operands into 32-bit signed integers before running bitwise operators. >> sign-extends; >>> zero-extends.Beware the 53-bit boundary: Never parse 64-bit IDs or large hashes with parseInt() or floating-point primitives—always enforce BigInt.Use Base-36 for safe slugification: When you need human-readable, URL-safe, compact sequential keys without case-sensitivity bugs, reach for Radix 36 before Base-64.

Share:WhatsAppFacebookX (Twitter)Telegram

Read Complete Original Article

Published originally on HackerNoon

Visit Publisher Site →

Related News

🇺🇸 United States
NASA hopes the commercial space industry can help save Hubble
Technology1h ago

NASA hopes the commercial space industry can help save Hubble

Gravity is a cruel mistress, slowly dragging the famed telescope ever closer to its ultimate demise with every day. Hubble is in no immediate danger of burning up in the Earth's atmosphere, but its time will run out in the early to mid 2030s unless its orbit is boosted. So NASA is taking proposals from the private sector to see if anyone can save it. In a post on NASA's site, Administrator Jared Isaacman said: "Hubble still has important science ahead of it, and we should be looking at every rea

The Verge
Multikhabar App

All Information In One Place

Real-time headlines from trusted sources. Get the app now — stay updated, inspired, and ahead with breaking news!

Get it on Google Play