1

I'm trying to convert hex strings to double values, where some of the values are negative. This answer works fine for positive double hex strings, but when the double is negative I get a std::out_of_range exception. Why is that? Is there a way to get around it?

I have also tried to do the same thing with stringstream, and that works just fine for both positive and negative hex strings. So there is always that solution, but to me the union approach looks a bit more elegant. A matter of taste I guess.

Sample code:

#include <string>
#include <iostream>
#include <iomanip>
#include <sstream>


int main()
{
    std::string str1("3fb999999999999a"); //  0.1
    std::string str2("bfb999999999999a"); // -0.1


    // ******************************
    // UNION
    // ******************************
    union uint64double
    {
        unsigned long long i;
        double    d;
    };

    uint64double val1;
    val1.i = std::stoll(str1, nullptr, 16);

    uint64double val2;
    val2.i = std::stoll(str2, nullptr, 16);     // This throws std::out_of_range exception

    const int w = 18;

    std::cout << "USING UNION" << std::endl;
    std::cout << std::setw(w) << "HEX" << std::setw(w) << "DOUBLE" << std::endl;
    std::cout << std::setw(w) << str1 << std::setw(w) << val1.d << std::endl;
    std::cout << std::setw(w) << str2 << std::setw(w) << val2.d << std::endl;

    std::cout << std::endl;


    // ******************************
    // STRINGSTREAM
    // ******************************
    unsigned long long i1;
    std::stringstream ss;
    ss << std::hex << str1;
    ss >> i1;
    double d1(reinterpret_cast<double&>(i1));

    unsigned long long i2;
    ss.clear();
    ss << std::hex << str2;
    ss >> i2;
    double d2(reinterpret_cast<double&>(i2));

    std::cout << "USING STRINGSTREAM" << std::endl;
    std::cout << std::setw(w) << "HEX" << std::setw(w) << "DOUBLE" << std::endl;
    std::cout << std::setw(w) << str1 << std::setw(w) << d1 << std::endl;
    std::cout << std::setw(w) << str2 << std::setw(w) << d2 << std::endl;

    std::cout << std::endl;

    return 0;
}

Output:

USING UNION
           HEX            DOUBLE
  3fb999999999999a               0.1
  bfb999999999999a           1.#QNAN

USING STRINGSTREAM
               HEX            DOUBLE
  3fb999999999999a               0.1
  bfb999999999999a              -0.1

I'm using VS2012

Community
  • 1
  • 1
Elfendahl
  • 115
  • 1
  • 9

2 Answers2

2

Of course stoll throws std::out_of_range exception on input "bfb999999999999a", because 0xbfb999999999999a > LLONG_MAX. If you use stoull instead, everything works nicely, because 0xbfb999999999999a <= ULLONG_MAX.

TonyK
  • 16,761
  • 4
  • 37
  • 72
1

You can pass the hex string into the stringstream constructor to clean it up a bit. Your std::hex is in the wrong place. std::hex tells the stream to treat the input as a hex value, rather than just a bunch of characters.

I would put this functionality in its own function too, to make it cleaner:

#include <string>
#include <iostream>
#include <iomanip>
#include <sstream>

double hexToDouble(const std::string& hex)
{
    double d = 0;
    std::stringstream ss(hex);
    ss >> std::hex >> reinterpret_cast<uint64_t&>(d); // I feel dirty
    return d;
}

int main()
{
    std::string str1("3fb999999999999a"); //  0.1
    std::string str2("bfb999999999999a"); // -0.1

    std::cout << str1 << ' ' << hexToDouble(str1) << std::endl;
    std::cout << str2 << ' ' << hexToDouble(str2) << std::endl;

    return 0;
}
Julian
  • 1,688
  • 1
  • 12
  • 19