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);
CodePudding user response:
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:
#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