简体   繁体   中英

C - fail to convert unsigned char array to double

I would like to convert a unsigned char array to double in C. I tried many way to do so but still wrongly converted to 0.00000.

union {
   double longtitude;
   unsigned char bytes[sizeof(double)];
}u;
unsigned char * receive_buffer = malloc(65536);
int recv = recv(fd,receive_buffer,65536,0); 
// the buffer should has 8 byte value  {40 5c 80 23 8d a3 c2 12}

memcpy(&u.bytes,receive_buffer,sizeof(double));  // copy to char array
for ( int i=8;i>=0;i--){
        u.longtitude = u.bytes[i]; 
    }
printf("%lf",u.longtitude);  // the result is 0.000000 / the expected result should be 114.00217

I got the result of '0.000000' from above code that I found from internet. How can I convert the char array to double? What's wrong in above code?

UPDATE

I added more specific code above. I have checked the contents of the receive_buffer and it contains the value in the above comment. The u.bytes correctly gets a value from the buffer via memcpy . The union and for loop part is the way I found from other similar questions. I tried it, but got result = 0.000000. Sorry about the unclear code posted and problem stated before; I am quite new to C language.

Delete the for loop.

The memcpy copies the bytes from the buffer into the bytes array of the union. Those are the same bytes used for the longitude member, so they are already in place. You do not need the for loop to copy those bytes, and it was incorrectly writing the values of the bytes into the value of the double rather than into the bytes that represent the value. Also, the loop index was wrong, as it was using 8 in the first iteration, but the bytes in an eight-byte object are indexed 0 to 7.

More than that, in C, you can modify the bytes that repesent an object with either a union or a memcpy . You do not need both. After the recv , this suffices:

double longitude;
memcpy(&longitude, receive_buffer, sizeof longitude);

I expect you could even do the recv directly into &longitude .

Remove the for loop entirely. I'm not sure what your intent in having it there is, but it undoes (or rather just clobbers) the work you just did.

Based on your update and the expected value of 114.00217 , the issue is endian-ness. Whatever machine you are getting the value from does not have the same endianess as your machine. So after converting to double, swap endianess.

// endian_swap() function taken from https://stackoverflow.com/a/21507710/669576
// I modified to work with double
double endian_swap(double d)
{
    long x = *(long *)&d;
    x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;
    x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;
    x = (x & 0x00FF00FF00FF00FF) << 8  | (x & 0xFF00FF00FF00FF00) >> 8;

    return *(double *)&x;
}

int main(void) {
    unsigned char s[] = {0x40,0x5c,0x80,0x23,0x8d,0xa3,0xc2,0x12};
    double d;
    // Just copy - no union needed
    memcpy(&d, s, sizeof(d));
    // Swap endianess
    d = endian_swap(d);
    printf("%lf\n", d);
    return 0;
}

Output: 114.002170

I don't do a lot of network programming, but I believe the ntohl() function is used for this. However, I don't know if there is a 64-bit version.


Update: Also, you can use htobe64() (Thanks to @Stargateur):

#include <endian.h>

int main(void) {
    unsigned char s[] = {0x40,0x5c,0x80,0x23,0x8d,0xa3,0xc2,0x12};
    // htobe64() expects a long, so use that first
    long l;
    memcpy(&l, s, sizeof(l));
    l = htobe64(l);
    // Then copy the swapped long bits to a double
    double d;
    memcpy(&d, &l, sizeof(d));
    printf("%lf\n", d);
    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM