7

I have a IEEE754 Double precision 64-bit binary string representation of a double number. example : double value = 0.999; Its binary representation is "0011111111101111111101111100111011011001000101101000011100101011"

I want to convert this string back to a double number in c++. I dont want to use any external libraries or .dll's as my program would operate in any platform.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
jero2rome
  • 1,548
  • 1
  • 21
  • 39
  • Read http://en.wikipedia.org/wiki/Binary_numeral_system#Fractions_in_binary Once you understand the theory crafting an algorithm should be simple. – Casey Robinson Dec 23 '11 at 13:24
  • I guess one question is whether the C standard guarantees that any specific size of `int` matches the size of `double`. (I'm not a "standard lawyer" and don't know if this is the case or not.) If not, then the job is significantly tougher. – Hot Licks Dec 23 '11 at 14:52

5 Answers5

12

C string solution:

#include <cstring>   // needed for all three solutions because of memcpy

double bitstring_to_double(const char* p)
{
    unsigned long long x = 0;
    for (; *p; ++p)
    {
        x = (x << 1) + (*p - '0');
    }
    double d;
    memcpy(&d, &x, 8);
    return d;
}

std::string solution:

#include <string>

double bitstring_to_double(const std::string& s)
{
    unsigned long long x = 0;
    for (std::string::const_iterator it = s.begin(); it != s.end(); ++it)
    {
        x = (x << 1) + (*it - '0');
    }
    double d;
    memcpy(&d, &x, 8);
    return d;
}

generic solution:

template<typename InputIterator>
double bitstring_to_double(InputIterator begin, InputIterator end)
{
    unsigned long long x = 0;
    for (; begin != end; ++begin)
    {
        x = (x << 1) + (*begin - '0');
    }
    double d;
    memcpy(&d, &x, 8);
    return d;
}

example calls:

#include <iostream>

int main()
{
    const char * p = "0011111111101111111101111100111011011001000101101000011100101011";
    std::cout << bitstring_to_double(p) << '\n';

    std::string s(p);
    std::cout << bitstring_to_double(s) << '\n';

    std::cout << bitstring_to_double(s.begin(), s.end()) << '\n';
    std::cout << bitstring_to_double(p + 0, p + 64) << '\n';
}

Note: I assume unsigned long long has 64 bits. A cleaner solution would be to include <cstdint> and use uint64_t instead, assuming your compiler is up to date and provides that C++11 header.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • Why not `return *reinterpret_cast(&x);` instead of memcpy()? It seems like one of the few cases where reinterpret_cast actually makes sense. – Adam Zalcman Dec 23 '11 at 13:46
  • @Adam: Because `memcpy` doesn't invoke UB. More info [here](http://stackoverflow.com/questions/4328342/). – fredoverflow Dec 23 '11 at 13:51
  • A strict C++ compiler will generate warnings, because you can't "just" copy bytes into an integral type like double. Of course it will still work, but if you want to be very standard compliant, I'd recommend using a union comprised of a byte array or integer type and a dummy variable of the target type. Then you shift the bits into the byte array like in the example and return the dummy variable. Ex.: `union { uint64_t src; double dst; } c; while (*s) { c.src = (c.src << 1) + (*s++ - '0'); } return c.dst;` – onitake Dec 23 '11 at 14:20
  • 1
    @onitake: Writing to one union member and then reading from another invokes undefined behavior. – fredoverflow Dec 23 '11 at 14:25
  • @FredOverflow: Thanks for the hint, I didn't know that. So, in general there is no "good" way to work around strict aliasing. Understandable, as it's not meant to be, but is there any other way for interpreting integer types as floating point? – onitake Dec 23 '11 at 14:41
  • @onitake: That's exactly what I asked [here](http://stackoverflow.com/questions/4328342/). – fredoverflow Dec 23 '11 at 14:46
  • `memcpy` between different types is no more "undefined behavior" than aliasing. – Hot Licks Dec 23 '11 at 14:49
  • @FredOverflow Thank you very much. Your code works just awesome. Could you tell me if i have a string s = "0011111111101111111101111100111011011001000101101000011100101011" directly. should i have to use s.c_str() to get the const char* and pass it to the function or is there any way to operate on the string directly. pardon me if this question is really dumb.. thanks – jero2rome Dec 23 '11 at 16:18
  • 1
    Calling `c_str()` would work just fine, but I added a `std::string` solution anyway, just in case. And a generic solution, this is C++ we're talking about, after all ;) – fredoverflow Dec 23 '11 at 16:35
1

A starting point would be to iterate through the individual characters in the string and set individual bits of an existing double.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
0

Is it really a character string of binary bits? If so, first convert to a 64-bit int. Then either use a library routine (probably there is one somewhere), or more simply, use a double aliased over the 64-bit int to convert to double.

(If it's already a 64-bit int then skip the first step.)

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
0

Ignoring byte-ordering issues, but I suppose this should be a viable option:

The below has an outcome of .999 on i386 with gcc. See it live: https://ideone.com/i4ygJ

#include <cstdint>
#include <sstream>
#include <iostream>
#include <bitset>

int main()
{
    std::istringstream iss("0011111111101111111101111100111011011001000101101000011100101011");

    std::bitset<32> hi, lo;
    if (iss >> hi >> lo)
    {
        struct { uint32_t lo, hi; } words = { lo.to_ulong(), hi.to_ulong() };
        double converted = *reinterpret_cast<double*>(&words);

        std::cout << hi << std::endl;
        std::cout << lo << std::endl;
        std::cout << converted << std::endl;
    }
}
sehe
  • 374,641
  • 47
  • 450
  • 633
0

my program would operate in any platform

Included I assume those whose double format isn't IEEE. Something like this should work:

#include <math.h>

...

int const dbl_exponent_bits = 11;
int const dbl_exponent_offset = 1023;
int const dbl_significand_bits = 52;

bool negative = (*num++ == '1');
int exponent = 0;
for (int i = 0; i < dbl_exponent_bits; ++i, ++num) {
    exponent = 2*exponent + (*num == '1' ? 1 : 0);
}
double significand = 1;
for (int i = 0; i < dbl_significand_bits; ++i, ++num) {
    significand = 2*significand + (*num == '1' ? 1 : 0);
}
assert(*num == '\0');
double result = ldexp(significand, exponent-(dbl_exponent_offset+dbl_significand_bits));
if (negative)
    result = -result;
AProgrammer
  • 51,233
  • 8
  • 91
  • 143
  • hello, in your code what is the data type for negative, exponent, significand, result....I'm getting lot of errors while compiling... – jero2rome Dec 23 '11 at 16:04