12

I am having trouble converting a int64_t to a char array and back. I don't know what is wrong with the code below, it makes complete logical sense to me. The code works for a as shown, but not the second number b which clearly falls into the range of int64_t.

#include <stdio.h>
#include <stdint.h>

void int64ToChar(char mesg[], int64_t num) {
  for(int i = 0; i < 8; i++) mesg[i] = num >> (8-1-i)*8;
}

int64_t charTo64bitNum(char a[]) {
  int64_t n = 0;
  n = ((a[0] << 56) & 0xFF00000000000000U)
    | ((a[1] << 48) & 0x00FF000000000000U)
    | ((a[2] << 40) & 0x0000FF0000000000U)
    | ((a[3] << 32) & 0x000000FF00000000U)
    | ((a[4] << 24) & 0x00000000FF000000U)
    | ((a[5] << 16) & 0x0000000000FF0000U)
    | ((a[6] <<  8) & 0x000000000000FF00U)
    | ( a[7]        & 0x00000000000000FFU);
  return n;
}

int main(int argc, char *argv[]) {
  int64_t a = 123456789;
  char *aStr = new char[8];
  int64ToChar(aStr, a);
  int64_t aNum = charTo64bitNum(aStr);
  printf("aNum = %lld\n",aNum);

  int64_t b = 51544720029426255;
  char *bStr = new char[8];
  int64ToChar(bStr, b);
  int64_t bNum = charTo64bitNum(bStr);
  printf("bNum = %lld\n",bNum);
  return 0;
}

output is

aNum = 123456789
bNum = 71777215744221775

The code also gives two warnings that I don't know how to get rid of.

warning: integer constant is too large for ‘unsigned long’ type
warning: left shift count >= width of type
user1161604
  • 353
  • 3
  • 4
  • 10
  • 3
    The second warning is the key. Operands of `<<` are promoted to `int`, so `a[0]<<56` will be always 0. Try `((int64_t)a[0])` etc. The first warning is about 51544720029426255, you need to say 51544720029426255LL. – n. m. could be an AI Mar 14 '12 at 04:09

4 Answers4

11

This is rather simple, the problem is that you are shifting bits in the char array but size of a[i] is 4 byes (upcast to int), so your shift just goes over range. Try replacing this in your code:

int64_t charTo64bitNum(char a[]) {
  int64_t n = 0;
  n = (((int64_t)a[0] << 56) & 0xFF00000000000000U)
    | (((int64_t)a[1] << 48) & 0x00FF000000000000U)
    | (((int64_t)a[2] << 40) & 0x0000FF0000000000U)
    | (((int64_t)a[3] << 32) & 0x000000FF00000000U)
    | ((a[4] << 24) & 0x00000000FF000000U)
    | ((a[5] << 16) & 0x0000000000FF0000U)
    | ((a[6] <<  8) & 0x000000000000FF00U)
    | (a[7]        & 0x00000000000000FFU);
  return n;
}

In this way you'll cast the char to a 64bit number before doing the shift and you won't go over range. You'll obtain correct results:

entity:Dev jack$ ./a.out 
aNum = 123456789
bNum = 51544720029426255

Just a side note, I think this would work fine too, assuming you don't need to peek inside the char array:

#include <string.h>

void int64ToChar(char a[], int64_t n) {
  memcpy(a, &n, 8);
}

int64_t charTo64bitNum(char a[]) {
  int64_t n = 0;
  memcpy(&n, a, 8);
  return n;
}
Jack
  • 131,802
  • 30
  • 241
  • 343
  • I'd prefer your alternative, memcpy to the int64_t variable directly, but I don't think it will work, eg: char *szInput = "51544720029426255"; /* 17 bytes, more than 8 bytes */ memcpy( &n, szInput, 8); will result in overflow, don't you think? Thanks – Zennichimaro Jan 16 '13 at 02:05
  • No, because you are specifying that you will copy just 8 bytes in any case through third parameter. – Jack Jan 16 '13 at 04:57
  • 1
    oh ya.. but then it will only copy the first 8 bytes, cout << n; will print: 51544720, right? 51544720029426255 should hold in int64_t as the maximum value it can hold is 9223372036854775807. – Zennichimaro Jan 16 '13 at 10:47
  • 1
    Value would be totally different, you are storing 8 ASCII characters into a 64 bit value but the inner number value will be printed according to the internal representation of the conversion of the 8 characters into a integer representation so there is not direct conversion. To convert a string literal to a u64 (which is not just storing it in an u64) you should use an appropriate function as `atoul`. – Jack Jan 16 '13 at 15:00
1
void int64ToChar(char mesg[], int64_t num) {
    *(int64_t *)mesg = num; //or *(int64_t *)mesg = htonl(num);

}
Bo Persson
  • 90,663
  • 31
  • 146
  • 203
gdj
  • 61
  • 1
0

Are you on a 32bit machine? IIRC I think that the U suffix only means either 'unsigned int' or 'unsigned long', both of which are 32bits on a 32bit machine. I think you want a 'unsigned long long' (64bit) literal in your bit shifts, or 0xFF00000000000000ULL

http://en.kioskea.net/faq/978-c-language-handling-64-bit-integers#unsigned-64-bit-integer

Tim
  • 35,413
  • 11
  • 95
  • 121
0

In charTo64bitNum you need to cast the char to 64-bit before you shift it:

(((int64_t)a[0] << 56) & 0xFF00000000000000U)
Andrew Cooper
  • 32,176
  • 5
  • 81
  • 116