How to convert huge numbers of boost multiprecision (cpp_bin_float) efficiently?

98 views Asked by At

I have an "extented double" data-type that stores the exponent in a seperate int variable to increase the range like this:

struct extDouble
{
    double Value;
    int Exponent;
}

I now need to convert a boost multiprecision float (cpp_bin_float) into this data-type. For values within the range of doubles this is no problem and can be done with

extDouble Number;
Number.Value = BigNumber.convert_to<double>();
Number.Exponent = 0;

(the loss of decimal places is no problem).

But how could I (efficiently) do this with values beyond the range of doubles? I know I could read the exponent directly like this:

Number.Exponent = BigNumber.backend().exponent();

But in order to get the fractional value, I would have to multiply BigNumber by pow(2, Exponent) which I want to avoid for performance reasons. Is there a way to read out the fractional value directly or simply set the exponent of BigNumber to 0? Or does anybody have another idea how to accomplish the whole thing as efficiently as possible (something like frexp or ldexp for boost multiprecision)?

Edit:

To clarify: if there was frexp for boost multifloats, I would simply do something like this:

Number.Value = frexp(BigNumber, &Number.Exponent);
1

There are 1 answers

1
sehe On BEST ANSWER

I thought I was going to post a clever answer based in ilogb/scalbn¹, but then I thought to just check the premise:

extDouble toExt(F number) {
    extDouble n;
    n.Value = frexp(number, &n.Exponent).convert_to<double>();
    return n;
}

Works just fine. frexp is found via ADL, and resolves to the multiprecision implementation.

Generalized a bit and with test cases:

Live On Coliru

#include <boost/multiprecision/cpp_bin_float.hpp>
using F = boost::multiprecision::cpp_bin_float_50;

struct extDouble {
    double Value;
    int Exponent;

    friend auto& operator<<(std::ostream& os, extDouble const& ed) {
        return os << "{Value:" << ed.Value << ", Exponent:" << ed.Exponent << "}";
    }
};

template <typename F> extDouble toExt(F number) {
    extDouble n;
    n.Value = frexp(number, &n.Exponent).template convert_to<double>();
    return n;
}

int main() {
    using L = std::numeric_limits<double>;
    std::cout << "max exponent: " << L::max_exponent << "\n";
    std::cout << "mix exponent: " << L::min_exponent << "\n";

    for (F f :
         {
             F("123.45"),
             F("123.45") * pow(F(2), L::max_exponent - 5),
             F("123.45") * pow(F(2), L::min_exponent + 3),
             F("123.45") * pow(F(2), L::max_exponent * 2),
             F("123.45") * pow(F(2), L::min_exponent * 2),
         }) //
    {
        std::cout << f << " -> " << toExt(f) << std::endl;
    }
}

Prints

max exponent: 1024
mix exponent: -1021
123.45 -> {Value:0.964453, Exponent:7}
6.93516e+308 -> {Value:0.964453, Exponent:1026}
4.39497e-305 -> {Value:0.964453, Exponent:-1011}
3.98953e+618 -> {Value:0.964453, Exponent:2055}
2.44478e-613 -> {Value:0.964453, Exponent:-2035}

¹ much like here https://en.cppreference.com/w/cpp/numeric/math/frexp