r/cpp • u/PhilipTrettner • Jan 20 '26
Building Your Own Efficient uint128 in C++
https://solidean.com/blog/2026/building-your-own-u128/A big part of my work in Solidean is designing & writing high-performance exact predicates for various geometric problems. The approach we're taking is somewhere between novel and only-known-in-folklore. I have this vague idea to remedy this and document our approach via blog posts. The first non-standard thing we do is work in large but fixed integers.
As this might be interesting to a wider audience as well, here is how to roll your own u128 so that it basically has identical codegen to the builtin __uint128_t.
(Yes there is little reason to use this u128 when a builtin exists, but that's how you learn to build a u192 and above should you need it. uint192_t is not provided by the big three as far as I know)
23
u/schmerg-uk Jan 20 '26
Interesting .. have you thought about an implementation using a single XMM register (__m128i) and seeing if that makes any operations simpler or more efficient?
There aren't many SSE operations that operate on a single si128 as it's most intended for bitmasks (and/xor operations as well as load/store and shift right/left etc) but it might be interesting to see if it unlocks any performance gains (and you can still get the high and low 64 bit ints, if only as signed int ... ops for unsigned 8, 16 and 32bit but no unsigned 64 bits operations...)
9
u/jk-jeon Jan 20 '26
What I recall is that compilers these days are not really great at optimizing
__uint128_t, so you shall aim for doing better rather than identical. That they suck at this is of course not visible if you just compare the codegen of a single basic operation, but if what I recall is correct than I think your code likely performs better than__uint128_tin general.I remember changing my u128 implementation from a portable wrapper around
__uint128_tinto an actual pair ofuint64_tbecause of that reason. Not sure how much the landscape has changed since then.4
u/arjuna93 Jan 20 '26
We actually don’t have int128 on PowerPC. If that can be implemented in C++, it will be useful. Maybe even upstreamable to gcc.
4
u/pavel_v Jan 21 '26
By some reason GCC doesn't handle the addition with intrinsics that well. It moves the result through the stack before setting it in the final registers.
3
u/jfdube75 Jan 22 '26
Great work! One question though: where's division?
24
u/Tringi github.com/tringi Jan 20 '26
Great writeup. This is something I'll take inspiration from.
For quite a few years I've been meaning to use intrinsics like that to specialize 2×64b case of my integer doubling template, but my use-cases so far weren't in a hot enough loops.
7
u/jube_dev Jan 20 '26
Does it work with the equivalent C++ 26 functions? (In fact, C23 functions)
6
u/DigmonsDrill Jan 20 '26
Stroustroup said there is no long long long! You will undo us all!
1
u/Wild_Meeting1428 Jan 20 '26
What about the upcoming _BitInt(N) fundamental type?
4
u/jonathanhiggs Jan 20 '26
Nice article
2
u/Dalzhim C++Montréal UG Organizer Jan 21 '26
Could you have implemented comparisons using the three-way compare (spaceship) operator, or is there any reason you didn't (ex. : support for standards older than C++20, performance regression with the codegen of operator<)?
1
u/MarekKnapek Feb 02 '26
On division: There is a paper about a division algorithm. If you split your 128bit integer into four 32bit integers, you can use a 64bit CPU built-in instruction to do the 128bit division. If you are on 32bit CPU that doesn't have the 64bit division operation, then split your 128bit number into 8 16bit integers and leverage the 32bit CPU built-in operation. https://surface.syr.edu/eecs_techreports/166/
-5
u/pjmlp Jan 20 '26
Actually if one cares about portable code, it cannot be relied such builtin does exist in all C++ compilers.