Representing Large Fixed-Width Integers in C++
Large integers are common in many applications. In cryptography, for example, integers of 1,000 bits and larger are not uncommon. The implementation I presented here is based on a binary representation of numbers using a bitset, at a cost of some performance. What I lost in performance I more than made up for in simplicity. A more efficient implementation of arbitrary precision numbers could easily fill the book.
The BigInt template below uses the bitset from the header to (from my last post) allow you to represent unsigned integers using a fixed number of bits specified as a template parameter.
//big_int.hpp #ifndef BIG_INT_HPP #define BIG_INT_HPP #include <bitset> #include "bitset_arithmetic.hpp" // from my last post of bitset manipulation. template<unsigned int N> class BigInt { typedef BigInt self; public: BigInt( ) : bits( ) { } BigInt(const self& x) : bits(x.bits) { } BigInt(unsigned long x) { int n = 0; while (x) { bits[n++] = x & 0x1; x >>= 1; } } explicit BigInt(const std::bitset& x) : bits(x) { } // public functions bool operator[](int n) const { return bits[n]; } unsigned long toUlong( ) const { return bits.to_ulong( ); } // operators self& operator<<=(unsigned int n) { bits <<= n; return *this; } self& operator>>=(unsigned int n) { bits >>= n; return *this; } self operator++(int) { self i = *this; operator++( ); return i; } self operator--(int) { self i = *this; operator--( ); return i; } self& operator++( ) { bool carry = false; bits[0] = fullAdder(bits[0], 1, carry); for (int i = 1; i < N; i++) { bits[i] = fullAdder(bits[i], 0, carry); } return *this; } self& operator--( ) { bool borrow = false; bits[0] = fullSubtractor(bits[0], 1, borrow); for (int i = 1; i < N; i++) { bits[i] = fullSubtractor(bits[i], 0, borrow); } return *this; } self& operator+=(const self& x) { bitsetAdd(bits, x.bits); return *this; } self& operator-=(const self& x) { bitsetSubtract(bits, x.bits); return *this; } self& operator*=(const self& x) { bitsetMultiply(bits, x.bits); return *this; } self& operator/=(const self& x) { std::bitset tmp; bitsetDivide(bits, x.bits, bits, tmp); return *this; } self& operator%=(const self& x) { std::bitset tmp; bitsetDivide(bits, x.bits, tmp, bits); return *this; } self operator~( ) const { return ~bits; } self& operator&=(self x) { bits &= x.bits; return *this; } self& operator|=(self x) { bits |= x.bits; return *this; } self& operator^=(self x) { bits ^= x.bits; return *this; } // friend functions friend self operator<<(self x, unsigned int n) { return x <<= n; } friend self operator>>(self x, unsigned int n) { return x >>= n; } friend self operator+(self x, const self& y) { return x += y; } friend self operator-(self x, const self& y) { return x -= y; } friend self operator*(self x, const self& y) { return x *= y; } friend self operator/(self x, const self& y) { return x /= y; } friend self operator%(self x, const self& y) { return x %= y; } friend self operator^(self x, const self& y) { return x ^= y; } friend self operator&(self x, const self& y) { return x &= y; } friend self operator|(self x, const self& y) { return x |= y; } // comparison operators friend bool operator==(const self& x, const self& y) { return x.bits == y.bits; } friend bool operator!=(const self& x, const self& y) { return x.bits != y.bits; } friend bool operator>(const self& x, const self& y) { return bitsetGt(x.bits, y.bits); } friend bool operator<(const self& x, const self& y) { return bitsetLt(x.bits, y.bits); } friend bool operator>=(const self& x, const self& y) { return bitsetGtEq(x.bits, y.bits); } friend bool operator<=(const self& x, const self& y) { return bitsetLtEq(x.bits, y.bits); } private: std::bitset<N> bits; };
The BigInt template class could be used to represent factorials, as shown below,
//Using the big_int class #include "big_int.hpp" #include <iostream> #include <vector> #include <iterator> #include <algorithm> using namespace std; void outputBigInt(BigInt<1024> x) { vector<int> v; if (x == 0) { cout << 0; return; } while (x > 0) { v.push_back((x % 10).to_ulong( )); x /= 10; } copy(v.rbegin( ), v.rend( ), ostream_iterator(cout, "")); cout << endl; } int main( ) { BigInt<1024> n(1); // compute 32 factorial for (int i=1; i <= 32; ++i) { n *= i; } outputBigInt(n); }
The program output is:
263130836933693530167218012160000000
Related articles
- Performing Arithmetic on Bitsets in C++ (alikhuram.wordpress.com)
- Binary Search Tree template Class (BST) (alikhuram.wordpress.com)
- Comparisons in C++ (davidcorne.com)
- C++ Primer Plus (6th ed.) CH 3: Dealing with Data (goneim.wordpress.com)
Posted on May 17, 2013, in Algorithms, C++, Data Structures and tagged 1000bit large integers, Arbitrary-precision arithmetic, BigInt, Bit array, bitset, Boolean data type, cryptography, factorial, Generic programming, Integer (computer science), Large integers, Relational operator, Representing Large Fixed-Width Integers in C++. Bookmark the permalink. 6 Comments.
Very nice! You could also use C++ 11 user defined literals (http://en.cppreference.com/w/cpp/language/user_literal) so you can write BigInt thirty_two_fact = 263130836933693530167218012160000000_bi;
But I’m not sure how that would work with the template argument.
Thank you David!
Yes. literals can be used and there is way around with the template arguments.
Pingback: Implementing Fixed-Point Numbers in C++ | Khuram Ali
Pingback: Khuram Ali
Pingback: Sorting Localized Strings | Khuram Ali
Pingback: Using Function Pointers for Callbacks in C++ | Khuram Ali