Jack Maloney
Jack Maloney

Reputation: 545

Convert 64bit Int to Char[] (and back)

I program that I would like to convert an array of big-endian (I believe that since I'm on a Mac, ints would be little-endian) chars (or rather uint8_ts) to an int64_t and back. Here is my code:

int64_t CharsToInt(uint8_t* chars) {
    return chars[0] + (chars[1] * 0x100) + (chars[2] * 0x10000) + (chars[3] * 0x1000000) + (chars[4] * 0x100000000) + (chars[5] * 0x10000000000) + (chars[6] * 0x1000000000000) + (chars[7] * 0x100000000000000);
}

void IntToChars(int64_t i, uint8_t* chars) {
    for(int k = 0; k < 8; k++) {
        chars[k] = i >> k*8;
    }
}

int main() {

    int64_t x;

    unsigned char chars[8];

    IntToChars(x, chars);

    for (int k = 0; k < 8; k++) {
        printf("%0x\n", chars[k]);
    }

    // little endian
    int64_t rv = CharsToInt(chars);

    printf("%lld\n", rv);
}

If x is 12, or any other zero or positive number, the code works perfectly fine, however if x is a negative number, it fails to work.

Output for x = 12:

c
0
0
0
0
0
0
0
value: 12

output for x = -12:

f4
ff
ff
ff
ff
ff
ff
ff
value: -4294967308

This seems to have something to do with the way the sign gets stored and converted, because I think Intel (I'm on a Mac) uses 2s-compliment instead of a plain old sign bit. However, I don't really know how to determine if this is true, and if it is, how to compensate for it (preferably in a portable way).

I know that there are a lot of other questions like this, and I've read through them (in fact most of this code is from them), but I still can't get it to work, so I asked my own.

Upvotes: 0

Views: 794

Answers (2)

Jack Maloney
Jack Maloney

Reputation: 545

After messing around with it some more, this is what I finally got working (I think). It actually avoids having to deal with endianness and 2s-compliment all together, by removing the sign and reapplying it after the conversion, using C masks and multiplication:

int64_t CharsToInt(uint8_t* chars) {
    // Don't modify argument
    uint8_t tmp;
    tmp = chars[0];
    bool neg = false;
    if (tmp & 0x80) {
        tmp &= 0x7f;
        neg = true;
    }

    int64_t rv = chars[7] + (chars[6] * 0x100) + (chars[5] * 0x10000) + (chars[4] * 0x1000000) + (chars[3] * 0x100000000) + (chars[2] * 0x10000000000) + (chars[1] * 0x1000000000000) + (tmp * 0x100000000000000);

    if (neg) {
        rv *= -1;
    }
    return rv;

}

void IntToChars(int64_t i, uint8_t* chars) {
    int64_t num = i;
    bool neg = false;
    if (i & 0x8000000000000000) {
        neg = true;
        num *= -1;
    }

    chars[0] = num / 0x100000000000000;
    num = num % 0x100000000000000;
    chars[1] = num / 0x1000000000000;
    num = num % 0x1000000000000;
    chars[2] = num / 0x10000000000;
    num = num % 0x10000000000;
    chars[3] = num / 0x100000000;
    num = num % 0x100000000;
    chars[4] = num / 0x1000000;
    num = num % 0x1000000;
    chars[5] = num / 0x10000;
    num = num % 0x10000;
    chars[6] = num / 0x100;
    num = num % 0x100;
    chars[7] = num;

    if (neg) {
        chars[0] += 0x80;
    }
}

Upvotes: 0

mziccard
mziccard

Reputation: 2178

You are right. Intel 64 and IA-32 use 2 complement representation of signed numbers. See Intel 64 and IA-32 Architectures Software Developer’s Manual Section 4.2.1. Reading FFFFFFFFFFFFFFF4 for -12 is therefore correct. In the 2 complement representation negative numbers are represented by taking the corresponding positive, inverting all the bits and adding 1:

12 = 000000000000000C -> FFFFFFFFFFFFFFF3 -> FFFFFFFFFFFFFFF4 = -12

If I can add something, you chould convert your char array to an uint64_t also by doing:

int64_t CharsToInt(uint8_t* chars) {
    return *(int64_t*)chars;
}

Upvotes: 1

Related Questions