64

What's the best way to represent a 128-bit number in C++? It should behave as closely to the built-in numeric types as possible (i.e. support all the arithmetic operators, etc).

I was thinking of building a class that had 2 64 bit or 4 32 bit numbers. Or possibly just creating a 128 bit block of memory and doing everything myself.

Is there some easier/more standard way, or something that I'm less likely to screw up when implementing it myself? :)

It would also be nice if it could be extended to 256-bit, 512-bit, etc...

David Coufal
  • 6,021
  • 5
  • 28
  • 30
  • If you need specifically 128-bit numbers, then that would be the way to go (unless you can find a library which has already done it for you, of course). But it sounds like you really want integers of arbitrary length, in which case a bigint library would make more sense. – jalf Jul 27 '09 at 15:52
  • 2
    I don't think he wants bigint. With arbitrary length comes a lot of overhead and complexity. He's probably just looking for a nice portable solution that would work for >128 in theory even if it's never needed. – Draemon Jul 27 '09 at 15:55
  • 1
    I added the >128 comment in an effort to make the question more generally relevant. I don't need it at this time. – David Coufal Jul 27 '09 at 16:14
  • GCC support: http://stackoverflow.com/questions/3329541/does-gcc-support-128-bit-int-on-amd64 – Ciro Santilli OurBigBook.com Oct 27 '15 at 04:55

11 Answers11

73

EDIT: when I first wrote this boost::multiprecision::uint128_t wasn't a thing yet. Keeping this answer for historical reasons.


I've made a uint128 class before, you can check it out at: http://www.codef00.com/code/uint128.h.

It is dependent on boost for automatically providing all of the variants of the math operators, so it should support everything a native unsigned int type does.

There are some minor extensions to built in types such as initializing it with a string like this:

uint128_t x("12345678901234567890");

There is a convenience macro which works similary to the ones in C99 which you can use like this:

uint128_t x = U128_C(12345678901234567890);
Evan Teran
  • 87,561
  • 32
  • 179
  • 238
  • 34
    Ah, the random downvote nearly 3 years after posting. Gotta love it. – Evan Teran Jan 17 '12 at 23:09
  • 4
    +1 @Evan Teran Here's your upvote for that freaked up guys's downvote :) – Shekhar_Pro Mar 15 '12 at 10:33
  • 11
    Could you make this class without boost ? I think including boost defeats the whole point of being "lightweight", and probably why of those downvoters and why this isnt accepted answer. – Rookie Jan 01 '13 at 10:03
  • 1
    Now I tried this class and found some problems: it goes to infinite loop if the value exceeds `2^127`, so i assumed it supports negative numbers too; surprisingly not; if i use negative number, or multiply a positive number by negative number, it gets to infinite loop again. – Rookie Jan 01 '13 at 13:38
  • 3
    @BradTilley at first I thought you were insulting him... then I saw his name... nice. – Justin Self Jan 30 '13 at 02:11
  • @Evan Teran: Nice! But it has a bug, it should be defined as: ` class uint128 : boost::shiftable < uint128, boost::totally_ordered < uint128, boost::integer_arithmetic < uint128, boost::bitwise < uint128, boost::unit_steppable< uint128 > > > > >`, otherwise sizeof( uint128 ) == 24 on at least MSVC 2013. ( sorry about the formating, can't seem to get it to work ). – Ylisar Jun 21 '14 at 15:46
  • @Ylisar: hmm, ok i'll take a look. perhaps virtual inheritance would be a good solution. I dunno, i'll see what the options are. – Evan Teran Jun 23 '14 at 13:25
  • @EvanTeran: IMO the solution I proposed would be a good fit, since that also respects the documentation of how `boost::operator` works. See `Base Class Chaining and Object Size` @ http://www.boost.org/doc/libs/1_55_0/libs/utility/operators.htm :) – Ylisar Jul 03 '14 at 00:34
  • 1
    if you are already using boost then you already have 128-bit integer types defined in boost/multiprecision/cpp_int.hpp as boost::multiprecision::int128_t (signed) and boost::multiprecision::uint128_t (unsigned) – Brian Jack Nov 02 '14 at 06:10
  • @BrianJack: you're right. When I wrote that class (and this answer) boost was probably around version 1.35.0 though, and it looks like `boost::multiprecision::uint128_t` was introduced around version 1.53.0. So this answer is a bit out of date, newer code can just use the boost classes. – Evan Teran Nov 03 '14 at 00:48
  • Nice lib, but multiplication is a little to slow for many practical applications. Also the boost dependency is annoying - boost already has boost::multiprecision. – N3UR0CHR0M Dec 12 '16 at 18:25
  • 1
    @N3UR0CHR0M well, when I wrote it and made this post, `boost::multiprecision::uint128_t` wasn't a thing yet. Wasn't going to delete this post just because boost finally decided to implement it :-) – Evan Teran Dec 13 '16 at 19:44
  • It looks like your test for carry in `operator+=()` in your class `uint128` is not totally reliable. See https://stackoverflow.com/questions/199333/how-do-i-detect-unsigned-integer-multiply-overflow/199455#199455 I got here searching for a simple class to do 128 bit integer arithmetic without having to add in Boost. – Richard Chambers Nov 07 '19 at 16:26
  • Can you define "not totally reliable"? – Evan Teran Nov 08 '19 at 19:13
24

This is somewhat of a special case, especially since you didn't specify what platform(s) you're looking for, but with GCC you can use what is called mode(TI) to get (synthesized) 128-bit operations, for instance:

   typedef unsigned int uint128_t __attribute__((mode(TI)));

   uint64_t x = 0xABCDEF01234568;
   uint64_t y = ~x;

   uint128_t result = ((uint128_t) x * y);

   printf("%016llX * %016llX -> ", x, y);

   uint64_t r1 = (result >> 64);
   uint64_t r2 = result;

   printf("%016llX %016llX\n", r1, r2);

This only works on 64-bit processors, though.

One way or another, you're looking at multiple precision arithmetic to solve this. mode(TI) will cause the compiler to generate the operations for you, otherwise they have to be written explicitly.

You can use a general bigint package; ones in C++ I know of include the number theory packages LiDIA and NTL, and the bigint packages used for cryptographic code in Crypto++ and Botan). Plus of course there is GnuMP, which is the canonical C MPI library (and it does have a C++ wrapper as well, though it seemed poorly documented last time I looked at it). All of these are designed to be fast, but are also probably tuned for larger (1000+ bit) numbers, so at 128 bits you may be dealing with a lot of overhead. (On the other hand you don't say if that matters or not). And all of them (unlike the bigint-cpp package, which is GPL, are either BSD or LGPL) - not sure if it matters - but it might matter a lot.

You could also write a custom uint128_t kind of type; typically such a class would implement much the same algorithms as a regular MPI class, just hardcoded to have only 2 or 4 elements. If you are curious how to implement such algorithms, a good reference is Chapter 14 of the Handbook of Applied Cryptography

Of course doing this by hand is easier if you don't actually need all the arithmetic operations (division and modulo, in particular, are rather tricky). For instance, if you just need to keep track of a counter which might hypothetically overflow 64 bits, you could just represented it as a pair of 64 bit long longs and do the carry by hand:

unsigned long long ctrs[2] = { 0 };

void increment() {
   ++ctrs[0];
   if(!ctrs[0]) // overflow
     ++ctrs[1];
}

Which of course is going to be a lot simpler to deal with than a general MPI package or a custom uint128_t class.

Jack Lloyd
  • 8,215
  • 2
  • 37
  • 47
18

Look into other libraries that have been developed. Lots of people have wanted to do this before you. :D

Try bigint C++

Gordon Gustafson
  • 40,133
  • 25
  • 115
  • 157
12

Boost has data types in multiprecision library for types ranging from 128 to 1024 bits.

#include <boost/multiprecision/cpp_int.hpp>

using namespace boost::multiprecision;

int128_t mySignedInt128 = -1;
uint128_t myUnsignedInt128 = 2;
int256_t mySignedInt256 = -3;
uint256_t myUnsignedInt256 = 4;
int512_t mySignedInt512 = -5;
uint512_t myUnsignedInt512 = 6;
int1024_t mySignedInt1024 = -7;
uint1024_t myUnsignedInt1024 = 8;
Tuxdude
  • 47,485
  • 15
  • 109
  • 110
  • As I tested in Boost `sizeof(uint512_t)` (and 1024) doesn't give 512/8 and 1024/8. It gives a bit bigger size. I looked into code of Boost and saw that it has two extra fields - sign and number of limbs. Obviously as 512 and 1024 are fixed-size numbers and also unsigned, sign and limbs cnt fields are not needed. But I didn't find any way how to remove this fields. In Boost there is a template argument `is_trivial` that can exclude this field, but there is not a single way how to use this argument, it is not exposed in external `uint512_t` definition. Maybe you know how to make fixed size? – Arty Aug 27 '22 at 05:52
7

GCC supports a 128-bit integer type for processors which support it. You can access it using:

__int128          a;
unsigned __int128 b;

02020-02-10 Update: according to this: GCC, Clang, and Intel ICC all support a built-in __int128 type.

Richard
  • 56,349
  • 34
  • 180
  • 251
  • As I know CLang had `using T = _ExtInt(12345);` to make fixed-size 12345-bit integer. But this was till version 13 of CLang. Version 14 has only 128 bit, _ExtInt doesn't work anymore. Do you know by any chance how to make fixed size 256/512/1024 bit integers? As I described in [my comment](https://stackoverflow.com/questions/1188939/representing-128-bit-numbers-in-c/28710094#comment129810387_28710094) if to use Boost's integers then it has extra fields that makes it non-plain, having extra size, more than amount of bits. Maybe you can suggest how to have 256/512/1024? – Arty Aug 27 '22 at 06:04
  • @Arty: please post this as an actual question. Feel free to send me a link to it. – Richard Aug 27 '22 at 06:24
  • As you suggested just now created [a question](https://stackoverflow.com/questions/73508901/). If you have any ideas please answer! Thanks! – Arty Aug 27 '22 at 06:41
4

Don't reinvent the wheel -- I'm positive other people have already solved this problem, although I can't name any solutions off the top of my head. GMP can surely solve your problem, although it's overkill for fixed-size integers, and it's also a little cumbersome to use (it's a C library, not C++).

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • GNUMP contains a C++ wrapper. – Axel Gneiting Aug 02 '10 at 13:46
  • I'm going to resist downvoting (because opinion), but "Don't reinvent the wheel" is the worst statement. It should instead be "Don't fix something, that don't need fixing". I've used and tested my fair share of commercially used and open-source libraries. The worst library, was one that was reading and processing a really simple file format. The library wasn't aware of page sizes, and as such loaded a 100 mb file in 121 seconds, which I got down to less than 5 seconds. – vallentin Oct 01 '16 at 15:46
  • don't tell c/c++ programmers not to reinvent the wheel, it's rude. – Dmytro Sep 06 '17 at 08:56
  • I really like this wheel: https://en.wikipedia.org/wiki/Reuleaux_triangle – Jesse Chisholm Dec 25 '20 at 20:32
2

You may want to try GMP

Burkhard
  • 14,596
  • 22
  • 87
  • 108
0

Here is a library I found on google.

http://sourceforge.net/projects/cpp-bigint/

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
0

You might be better off with an infinite-precision integer class, rather than a sequence of increasing size. Some languages (like Common Lisp and IIRC Python) have them natively. I'm not sure offhand what's available for C++; last I looked there wasn't a Boost version.

David Thornley
  • 56,304
  • 9
  • 91
  • 158
0

The cairo graphics library has two files which implement portable 128-bit integer arithmetic: cairo-wideint-private.h, cairo-wideint.c. We include just these two in our project to get 128-bits.

pdbj
  • 573
  • 5
  • 9
-3

In Visual Studio C++ there is a type FLOAT128 that is used to represent 128-bit integers. It is implemented as:

#if defined(_M_IA64) && !defined(MIDL_PASS)
__declspec(align(16))
#endif
typedef struct _FLOAT128 {
    __int64 LowPart;
    __int64 HighPart;
} FLOAT128;

so I'm not sure about what math operations are implemented for it

Jeff Leonard
  • 3,284
  • 7
  • 29
  • 27
  • 10
    Are you sure that a type called FLOAT is used to represent INTEGERS? Doesn't make much sense. – fortran Jul 27 '09 at 16:25
  • It's called FLOAT but it's composed of two 64-bit ints. The name doesn't make sense to me either, there's probably some historical reason for the misleading name. – Jeff Leonard Aug 03 '09 at 03:25
  • 3
    That's probably [Quadruple-precision floating-point format](http://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format), hence unrelated to what to UP wants, which is 128-bit **int**. The use of integers inside is obviously for the underlying bitwise and arithmetic operations as we don't need the 2 double values. Using 2 floating-point values is a very different format called [double-double](http://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format#Double-double_arithmetic) – phuclv Jan 23 '15 at 05:34
  • It can also be used for `fixed point floats` with the high 64-bits being the integer and the low 64-bits being the fraction. – Jesse Chisholm Dec 25 '20 at 20:34