Reputation: 545
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
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
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