9

I would like to add 2 arbitrarily sized integers in C++. How can I go about doing this?

jww
  • 97,681
  • 90
  • 411
  • 885
BUKA
  • 91
  • 1
  • 2
  • Please state more information.. Is a cast possible? – Betamoo May 28 '10 at 01:59
  • adding them is usually quite easy for most implementations of arbitrary sized integers. You just add them limb by limb starting at the low order end, adding in any carry from the previous step and computing the carry out. Just like you learned in elementary school. – President James K. Polk May 29 '10 at 02:22
  • You can have a look at [GMP](http://gmplib.org/), an arbitrary precision arithmetic library for C and C++. – Ismail Badawi May 28 '10 at 02:00
  • Something like [libgmp](http://gmplib.org/) will do arbitrary precision arithmetic. – WhirlWind May 28 '10 at 02:01
  • By default c++ does not have arbitrary sized integers, but plenty of 3rd party libraries exist. If you're still shopping for a library, consider Boost as well. – Chris Uzdavinis Sep 06 '17 at 18:18

3 Answers3

15

Here's an example showing how to use the OpenSSL bignum implementation for arbitrary-precision arithmetic. My example does 264 + 265. I'm using Linux.

#include <cstdio>
#include <openssl/crypto.h>
#include <openssl/bn.h>

int main(int argc, char *argv[])
{
        static const char num1[] = "18446744073709551616";
        static const char num2[] = "36893488147419103232";

        BIGNUM *bn1 = NULL;
        BIGNUM *bn2 = NULL;

        BN_CTX *ctx = BN_CTX_new();

        BN_dec2bn(&bn1, num1); // convert the string to BIGNUM
        BN_dec2bn(&bn2, num2);

        BN_add(bn1, bn1, bn2); // bn1 = bn1 + bn2

        char *result_str = BN_bn2dec(bn1);  // convert the BIGNUM back to string
        printf("%s + %s = %s\n", num1, num2, result_str);
        OPENSSL_free(result_str);

        BN_free(bn1);
        BN_free(bn2);
        BN_CTX_free(ctx);

        return 0;
}

It produces this output:

18446744073709551616 + 36893488147419103232 = 55340232221128654848

You need to have OpenSSL installed with the development libraries. If you have Linux, install the development library from your package manager and link with libcrypto.so.

g++ bignum.cpp -o bignum -lcrypto

Or download the OpenSSL source and build the static library libcrypto.a and link with it statically.

g++ bignum.cpp -o bignum -I./openssl-1.0.0/include ./openssl-1.0.0/libcrypto.a

On Windows, you'll need to install from the Windows port of OpenSSL.

indiv
  • 17,306
  • 6
  • 61
  • 82
  • 1
    What is the purpose of variable ctx in your example. It seems that it is never used ? – MadOgre Feb 05 '14 at 07:01
  • @MadOgre: Good observation. It turns out you don't need a context for `BN_add`, so it remains unused in this example. Other math operations do require it, like `BN_mul`, `BN_div`, `BN_mod`, and so forth. OpenSSL uses the context for internal bookkeeping. – indiv Feb 05 '14 at 17:17
  • Since you use the gnu compiler, I wouldn't use that kind of thing for such number, since there is a built-in type for 128-bits integer in your case. – user2284570 Jun 23 '14 at 00:28
  • Thanks for this example. With some minor changes I was able to compile this code also in ANSI C as `gcc test.c -o test -Wall -lcrypto -ansi -Wpedantic -Wextra`. – akarilimano Jul 15 '16 at 16:56
4

Using the + operator?

Bob Kaufman
  • 12,864
  • 16
  • 78
  • 107
Johan Kotlinski
  • 25,185
  • 9
  • 78
  • 101
4

I would like to add 2 arbitrarily sized integers in C++. How can I go about doing this?

If you want to perform the multi-precision math yourself, then I suggest you take a look at Donald Knuth's Art of Computer Programming. I believe Volume II, Seminumerical Algorithms, Chapter 4, Multiple Precision Arithmetic, is what you are interested in. Knuth gives you all the gory details.

Handling large numbers in C++? also provides a reference to an interesting paper. You should probably read it if you are rolling your own implementation. (Other similar questions lack the reference. Thanks to @herohuyongtao for providing it).

In addition to @indiv answer of using OpenSSL in C, you can use Botan or Crypto++. Both are C++ libraries, and both are about as old as OpenSSL. I'm surprised answers were not provided for them considering your question is tagged C++.

If you have C++11 or unique_ptr, then you can use them for the OpenSSL C code. unique_ptr really tidies things up. An example is given below.


Botan

Here is the program:

$ cat test.cxx
#include "botan/bigint.h"

#include <iostream>

int main()
{
    using Botan::BigInt;

    BigInt num1("18446744073709551616");
    BigInt num2("36893488147419103232");

    std::cout << num1 + num2 << std::endl;
    std::cout << std::hex << "0x" << num1 + num2 << std::endl;

    return 0;
}

And building from the library's directory (expediency):

$ g++ -I ./build/include test.cxx ./libbotan-2.a -o test.exe
$ ./test.exe
55340232221128654848
0x30000000000000000

Crypto++

Here is the program:

$ cat test.cxx
#include "cryptlib.h"
#include "integer.h"

#include <iostream>

int main()
{
    using CryptoPP::Integer;

    Integer num1("18446744073709551616");
    Integer num2("36893488147419103232");

    std::cout << num1 + num2 << std::endl;
    std::cout << std::hex << "0x" << num1 + num2 << std::endl;

    return 0;
}

And building from the library's directory (expediency):

$ g++ -I . test.cxx ./libcryptopp.a -o test.exe
$ ./test.exe
55340232221128654848.
0x30000000000000000h

OpenSSL

Here is the program for C++ and OpenSSL. The BN_obj "has a" BIGNUM, and it uses unique_ptr to manage the resources.

$ cat test.cxx
#include "openssl/bn.h"
#include "openssl/err.h"

#include <iostream>
#include <stdexcept>
#include <sstream>
#include <memory>
#include <new>

class BN_obj
{
using BN_ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
using BN_CTX_ptr = std::unique_ptr<BN_CTX, decltype(&::BN_CTX_free)>;

public:
    BN_obj() : m_bn(BN_Zero_Helper(), ::BN_free) {}
    BN_obj(const char* str) : m_bn(BN_Asciiz_Helper(str), ::BN_free) {}
    BN_obj(const BN_obj& obj) : m_bn(BN_Copy_Helper(obj.m_bn), ::BN_free) {}

    BN_obj& operator=(const BN_obj& obj) 
    {
        if(this != &obj)
            m_bn.reset(BN_dup(obj.m_bn.get()));

        return *this;
    }

    BN_obj Plus(const BN_obj& obj) const
    {
        BN_obj result;
        if (BN_add(result.m_bn.get(), m_bn.get(), obj.m_bn.get()) != 1)
        {
            std::ostringstream msg;
            unsigned long err = ERR_get_error();            
            msg << "BN_add failed, error 0x" << std::hex << err;
            throw std::runtime_error(msg.str());        
        }
        return result;
    }

    BN_obj Minus(const BN_obj& obj) const
    {
        BN_obj result;
        if (BN_sub(result.m_bn.get(), m_bn.get(), obj.m_bn.get()) != 1)
        {
            std::ostringstream msg;
            unsigned long err = ERR_get_error();            
            msg << "BN_sub failed, error 0x" << std::hex << err;
            throw std::runtime_error(msg.str());        
        }
        return result;
    }

    BN_obj Times(const BN_obj& obj) const
    {
        BN_obj result;
        BN_CTX_ptr ctx(BN_CTX_new(), ::BN_CTX_free);
        if (BN_mul(result.m_bn.get(), m_bn.get(), obj.m_bn.get(), ctx.get()) != 1)
        {
            std::ostringstream msg;
            unsigned long err = ERR_get_error();            
            msg << "BN_sub failed, error 0x" << std::hex << err;
            throw std::runtime_error(msg.str());        
        }
        return result;
    }

    friend std::ostream& operator<<(std::ostream& out, const BN_obj& obj);

protected:
    static BIGNUM* BN_Zero_Helper()
    {
        BIGNUM* z = BN_new();
        BN_zero(z);
        return z;
    }

    static BIGNUM* BN_Asciiz_Helper(const char* str)
    {
        BIGNUM* t = BN_new();
        if(!t)
            throw std::bad_alloc();
        if(BN_dec2bn(&t, str) == 0) {
            std::ostringstream msg;
            unsigned long err = ERR_get_error();            
            msg << "BN_dec2bn failed, error 0x" << std::hex << err;
            throw std::runtime_error(msg.str());
        }
        return t;
    }

    static BIGNUM* BN_Copy_Helper(const BN_ptr& obj)
    {
        return BN_dup(obj.get());
    }

private:
    BN_ptr m_bn;
};

BN_obj operator+(const BN_obj& a, const BN_obj& b) {
    return a.Plus(b);
}

BN_obj operator-(const BN_obj& a, const BN_obj& b) {
    return a.Minus(b);
}

BN_obj operator*(const BN_obj& a, const BN_obj& b) {
    return a.Times(b);
}

std::ostream& operator<<(std::ostream& out, const BN_obj& obj)
{
    const long f = out.flags() & std::ios::basefield;
    char* ptr = nullptr;

    if(f == std::ios::hex)
    {
        ptr = BN_bn2hex(obj.m_bn.get());
        out << ptr;
    }
    else if(f == std::ios::dec)
    {
        ptr = BN_bn2dec(obj.m_bn.get());
        out << ptr;
    }
    else
        throw std::runtime_error("Not implemented");

    if(ptr)
        OPENSSL_free(ptr);

    return out;
}

int main()
{
    const char z1[] = "18446744073709551616";
    const char z2[] = "36893488147419103232";

    BN_obj num1(z1);
    BN_obj num2(z2);

    std::cout << num1 + num2 << std::endl;
    std::cout << std::hex << "0x" << num1 + num2 << std::endl;

    return 0;
}

And building from the library's directory (expediency):

$  g++ -I ./include test.cxx ./libcrypto.a -o test.exe -ldl -pthread
$ ./test.exe
55340232221128654848
0x30000000000000000
jww
  • 97,681
  • 90
  • 411
  • 885