Nap Joseph Calub

Software Engineer and Open Source Enthusiast

0xffffffffffffffffn: uint64 hack to decrement by one using addition

Published on October 12, 2023



In the world of software development, there are often clever tricks and hacks that developers use to solve specific problems. One such trick involves using uint64 (unsigned 64-bit integer) arithmetic to subtract 1 from a number without actually using subtraction. This might sound confusing at first, but with a simple analogy and some examples, it becomes clear!

##The Toy Cash Register Analogy

Imagine a child's toy cash register that can only count up to 9. When it reaches 9, and you try adding 1 more, it wraps around and returns to 0. Now, if this toy didn't have a subtraction button, how could you subtract 1 from any number? The trick is to add 9! When you add 9, you essentially go full circle and land 1 number behind where you started.

##The Technical Explanation

In binary arithmetic, numbers are represented using bits. A uint64 number uses 64 bits. When you add two numbers and the result is too big to fit in 64 bits, it wraps around, similar to our toy cash register.

The magic number 0xffffffffffffffffn (which represents all 1s in binary for a 64-bit number) acts like the number 9 in our analogy. When you add this number to any uint64, it's equivalent to subtracting 1.

Bit Position:63-5655-4847-4039-3231-2423-1615-87-0
Previous Number: 30000000000000000000000000000000000000000000000000000000000000011
Magic Number to Add:1111111111111111111111111111111111111111111111111111111111111111
Result: 20000000000000000000000000000000000000000000000000000000000000010

###Example Code

1// Convention for BigInts is to add an "n" at the end.
2const previousNumber = 3n;
4// Hexadecimal representation of 64-bit magic number.
5const magicNumber = 0xffffffffffffffffn;
7// Convert to 64-bit unsigned integer.
8const result = BigInt.asUintN(64, previousNumber + magicNumber);
10console.log(result); // This will be 2n.

##Why is this Useful?

In the realm of programming, there are instances where certain operations, like subtraction, might be restricted due to system constraints. This "magic number" trick offers a clever workaround for such situations.

Take, for example, Deno KV. This key-value storage system in Deno provides an atomic sum operation, allowing you to add two Deno.KvU64 values. This is particularly handy for incrementing numeric counters in an ACID-compliant way. However, there's a catch: you can't directly decrement a value using this method since Deno.KvU64 requires a positive number in the construction.

Here's how you'd typically increment a counter:

1await kv
2 .atomic()
3 .mutate({
4 type: 'sum',
5 key: ['users_count'],
6 value: new Deno.KvU64(1n)
7 })
8 .commit();

But what if you wanted to decrease this counter? Enter our magic number trick. By using the magic number 0xffffffffffffffffn, you can effectively subtract from the counter:

1await kv
2 .atomic()
3 .mutate({
4 type: 'sum',
5 key: ['users_count'],
6 value: new Deno.KvU64(0xffffffffffffffffn)
7 })
8 .commit();

In essence, this approach leverages the properties of binary arithmetic to bypass system constraints. Pretty cool, right?


Understanding the underlying principles of binary arithmetic can open the door to many clever tricks in programming. The uint64 subtraction trick is just one example. Hopefully, you can find efficient solutions to complex problems by understanding and applying these concepts to your following projects.

© 2021 Nap Joseph Calub. All rights reserved.

Light Mode
Dark Mode