dakkan
dakkan

Reputation:

double to hex string & hex string to double

What I'm trying to do is to convert a double to hex string and then back to double.

The following code does conversion double-to-hex string.

char * double2HexString(double a)
{
   char *buf = new char[17]; // double is 8-byte long, so we have 2*8 + terminating \0
   char *d2c;
   d2c = (char *) &a;
   char *n = buf;
   int i;
   for(i = 0; i < 8; i++)
   {
      sprintf(n, "%02X", *d2c++);
      n += 2;
   } 
   *(n) = '\0';
}

This seems work, however, I'm not sure how to convert the resulting string back to double. Please advise :)

Upvotes: 7

Views: 27643

Answers (9)

Ali
Ali

Reputation: 58501

I am surprised to see nobody has come up with the standard solution, which is the %a format specifier in the ISO C99 Standard.

#include <iostream>
#include <string>
#include <stdio.h>

std::string double2hexastr(double d) {

  char buffer[25] = { 0 };

  ::snprintf(buffer, 25, "%A", d); // TODO Check for errors

  return buffer;
}

double hexastr2double(const std::string& s) {

  double d = 0.0;

  ::sscanf(s.c_str(), "%lA", &d); // TODO Check for errors

  return d;
}


int main() {

  std::cout << "0.1 in hexadecimal: " << double2hexastr(0.1) << std::endl;

  std::cout << "Reading back 0X1.999999999999AP-4, it is ";

  std::cout << hexastr2double("0X1.999999999999AP-4") << std::endl;

}

Upvotes: 10

Sai Krishna V
Sai Krishna V

Reputation: 1

I am using the below function, to convert double to hexadecimal.

char * double2HexString(double a) 
{ 
   char *buf = new char[17]; // double is 8-byte long, so we have 2*8 + terminating \0 
   char *d2c; 
   d2c = (char *) &a; 
   char *n = buf; 
   int i; 
   for(i = 0; i < 8; i++) 
   { 
      sprintf(n, "%02X", *d2c++); 
      n += 2; 
   }  
   *(n) = '\0'; 
}  

passing 1.0 & 16.0 to this function, the return values observed are 000000000000FF37 & 0000000000003040 respectively. I think we should get 1 & 10.

Upvotes: 0

RusVIPer
RusVIPer

Reputation: 11

For MFC, convert double to CString :)

CString MFCClass::DoubleToCString(double d, int beforKomma)
{
    char a[17]="0123456789ABCDEF";
    CString str = _T("");
    double dInt=0,dPunkt=0;
    bool is_otr=0;
    if (d<0) {is_otr=1; d=-d;}
    dPunkt = modf(d, &dInt);
    //целая часть
    long ld = (long)dInt;
    long mask = 0xf;
    int it;
    while(ld>0)
    {
        it = ld&mask;
        ld = ld>>4;
        str.Insert(0,a[it]);
    };
    // дробная часть
    //если целая часть 0:
    if (str.GetLength()==0) str += _T("0");
    str += _T(".");
    for (int i=0; i<beforKomma; i++)
    {
        dPunkt*=16;
        dPunkt = modf(dPunkt, &dInt);
        str += a[(int)dInt];
    }

    if (is_otr) str.Insert(0,_T("-"));
    return (str);
}

-345.86783907228863 -> "-159.DE2" (beforKomma=3)

Upvotes: 1

old_timer
old_timer

Reputation: 71566

You want to use a union and avoid this bad habit:

char *d2c;

d2c = (char *) &a;

For just printing its not bad, its when you try to modify d2c and then use a is when you get into trouble. (same is true for any two variables or pointers (or arrays) sharing the same (theoretical) memory.

union
{
    double f;
    unsigned long ul;
} myun;

myun.f = a;
printf("0x%lX",myun.ul);

to go the other way (scanf is also a very dangerous function that should be avoided).

myun.ul=strtoul(string,NULL,16);
a=myun.f;

Upvotes: 0

Sparr
Sparr

Reputation: 7722

#include <stdio.h>
main() {
  union double_ull_t {
    double d;
    unsigned long long u;
  } x;
  scanf("%lf",&x.d);
  printf("%016llX %lf\n",x.u,x.d);
  scanf("%016llX",&x.u);
  printf("%016llX %lf\n",x.u,x.d);
}

Maybe not the most efficient solution, but the easiest to code.

Upvotes: 0

strager
strager

Reputation: 90042

char *doubleToRawString(double x) {
    const size_t bytesInDouble = 8;

    union {
        double value;
        unsigned char bytes[bytesInDouble];
    } u;

    u.value = x;

    char *buffer = new char[bytesInDouble * 2 + 1];
    unsigned char *input = u.bytes;
    char *output = buffer;

    for(int i = 0; i < bytesInDouble; ++i) {
        sprintf(output, "%02hhX", *input);

        ++input;
        output += 2;
    }

    return buffer;
}

double rawStringToDouble(const char *input) {
    const size_t bytesInDouble = 8;

    union {
        double value;
        unsigned char bytes[bytesInDouble];
    } u;

    unsigned char *output = u.bytes;

    for(int i = 0; i < bytesInDouble; ++i) {
        sscanf(input, "%02hhX", output);

        input += 2;
        ++output;
    }

    return u.value;
}

This uses the non-standard hh modifier. If you don't want to use that, use:

unsigned int tmp = *input;
sprintf(output, "%02X", tmp);

unsigned int tmp;
sscanf(input, "%02X", &tmp);
*output = tmp;

Upvotes: 2

strager
strager

Reputation: 90042

char *doubleToRawString(double x) {
    // Assumes sizeof(long long) == 8.

    char *buffer = new char[32];
    sprintf(buffer, "%llx", *(unsigned long long *)&x);  // Evil!
    return buffer;
}

double rawStringToDouble(const char *s) {
    // Assumes sizeof(long long) == 8.

    double ret;
    sscanf(s, "%llx", (unsigned long long *)&ret);  // Evil!
    return ret;
}

Upvotes: 2

jpalecek
jpalecek

Reputation: 47770

Almost the same procedure should do

void hex2double(const char* buf, double& a)
{
   char tmpbuf[3]={0};
   char *d2c;
   unsigned int tmp;
   d2c = (char *) &a;
   char *n = buf;
   int i;
   for(i = 0; i < 8; i++)
   {
      tmpbuf[0]=*buf++;
      tmpbuf[1]=*buf++;
      sscanf(tmpbuf, "%X", &tmp);
      *d2c++=tmp;
   }
}

Quick & dirty.

Note, however, that this is playing with fire. First, your hex strings are only usable on machines with the same double format, and the same endianness. Second, the conversion functions are short on strict aliasing rule.

Upvotes: 0

Daniel C. Sobral
Daniel C. Sobral

Reputation: 297265

Using sprintf is slow, to be honest, but you can revert it with sscanf, doing almost exactly the same thing.

Well, actually, you'd have to copy each two characters to a buffer string, to decode each individually. My first try, below is incorrect:

double hexString2Double(char *buf)
{
  char *buf2 = new char[3];
  double a;
  char* c2d;
  c2d = (char *) &a;
  int i;

  buf2[2] = '\0'

  for(i = 0; i < 16; i++)
  {
    buf2[0] = *buf++;
    buf2[1] = *buf++;
    sscanf(buf2, "%X", c2d++);
  }

  return a;
}

You see, %X is decoded as an int, not as a byte. It might even work, depending on low-ending/high-endian issues, but it's basically broken. So, let's try to get around that:

double hexString2Double(char *buf)
{
  char *buf2 = new char[3];
  double a;
  char* c2d;
  c2d = (char *) &a;
  int i;
  int decoder;

  buf2[2] = '\0'

  for(i = 0; i < 16; i++)
  {
    buf2[0] = *buf++;
    buf2[1] = *buf++;
    sscanf(buf2, "%X", &decoder);
    c2d++ = (char) decoder;
  }

  return a;
}

Barring syntax errors and such, I think this should work.

Upvotes: 0

Related Questions