| Home | Libraries | People | FAQ | More

### Mixed Precision Arithmetic

Mixed precision arithmetic is fully supported by the library.

There are two different forms:

- Where the operands are of different precision.
- Where the operands are of the same precision, but yield a higher precision result.

##### Mixing Operands of Differing Precision

If the arguments to a binary operator are of different precision, then the operation is allowed as long as there is an unambiguous implicit conversion from one argument type to the other. In all cases the arithmetic is performed "as if" the lower precision type is promoted to the higher precision type before applying the operator. However, particular backends may optimise this and avoid actually creating a temporary if they are able to do so.

For example:

    mpfr_float_50         a(2), b;
    mpfr_float_100        c(3), d;
    static_mpfr_float_50  e(5), f;
    mpz_int               i(20);

    d = a * c;  // OK, result of operand is an mpfr_float_100.
    b = a * c;  // Error, can't convert the result to an mpfr_float_50 as it will lose digits.
    f = a * e;  // Error, operator is ambiguous, result could be of either type.
    f = e * i;  // OK, unambiguous conversion from mpz_int to static_mpfr_float_50

##### Operands of the Same Precision

Sometimes you want to apply an operator to two arguments of the same precision in such a way as to obtain a result of higher precision. The most common situation occurs with fixed precision integers, where you want to multiply two N-bit numbers to obtain a 2N-bit result. This is supported in this library by the following free functions:

    template <class ResultType, class Source1 class Source2>
    ResultType& add(ResultType& result, const Source1& a, const Source2& b);

    template <class ResultType, class Source1 class Source2>
    ResultType& subtract(ResultType& result, const Source1& a, const Source2& b);

    template <class ResultType, class Source1 class Source2>
    ResultType& multiply(ResultType& result, const Source1& a, const Source2& b);

These functions apply the named operator to the arguments a and b and store the result in result, returning result. In all cases they behave "as if" arguments a and b were first promoted to type `ResultType` before applying the operator, though particular backends may well avoid that step by way of an optimization.

The type `ResultType` must be an instance of class `number`, and the types `Source1` and `Source2` may be either instances of class `number` or native integer types. The latter is an optimization that allows arithmetic to be performed on native integer types producing an extended precision result.

For example:

    #include <boost/multiprecision/cpp_int.hpp>

    using namespace boost::multiprecision;

    boost::uint64_t i = (std::numeric_limits<boost::uint64_t>::max)();
    boost::uint64_t j = 1;

    uint128_t ui128;
    uint256_t ui256;
    //
    // Start by performing arithmetic on 64-bit integers to yield 128-bit results:
    //
    std::cout << std::hex << std::showbase << i << std::endl;
    std::cout << std::hex << std::showbase << add(ui128, i, j) << std::endl;
    std::cout << std::hex << std::showbase << multiply(ui128, i, i) << std::endl;
    //
    // The try squaring a 128-bit integer to yield a 256-bit result:
    //
    ui128 = (std::numeric_limits<uint128_t>::max)();
    std::cout << std::hex << std::showbase << multiply(ui256, ui128, ui128) << std::endl;

Produces the output:

    0xffffffffffffffff
    0x10000000000000000
    0xFFFFFFFFFFFFFFFE0000000000000001
    0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00000000000000000000000000000001

##### Backends With Optimized Mixed Precision Arithmetic

The following backends have at least some direct support for mixed precision arithmetic, and therefore avoid creating unnecessary temporaries when using the interfaces above. Therefore when using these types it's more efficient to use mixed precision arithmetic, than it is to explicitly cast the operands to the result type:

mpfr_float, gmp_float, cpp_int.