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 *
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;
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;
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;
}
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;
}
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)
#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.