I'm trying to store a state in my data logger. I can read/write fine to SD, but I can't wrap my head around reading/writing a long value correctly - I've build it down to converting it to char array and back.

My best try so far has been

long temp = 1418172669L;
unsigned char buf[4];
buf[0] = temp         & 0xFF;
buf[1] = (temp >>  8) & 0xFF;
buf[2] = (temp >> 16) & 0xFF;
buf[3] = (temp >> 24) & 0xFF;

long  l = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);

But l is very different from temp - currently the output is -26371. Am I making an obvious mistake?


I suggest using a union:

union {
    char myByte[4];
    long mylong;
} foo;

Char to Long:

Then you can just add the bytes:

foo myUnion;
myUnion.myByte[0] = buf[0];
myUnion.myByte[1] = buf[1];
myUnion.myByte[2] = buf[2];
myUnion.myByte[3] = buf[3];

Then you can access the long as:


Long to Char:

The same if you want to go the other way from long to char array:

foo myUnion
myUnion.myLong = 1234L;

To access bytes:

  • Thanks, this worked great (for longs, not for floats). Can you give me a documentation link for union?
    – Blitz
    Dec 10 '14 at 19:07
  • @LordT: I don't know why I used float as an example, I must have misread. I have no link to a "documentation" page, but this is a tutorial/example I think is good: tutorialspoint.com/cprogramming/c_unions.htm Dec 10 '14 at 19:20

I think the last expression is performed exclusively as an int expression and converted to long only at the end, i.e. before assignment to l.

long  l = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);

Here the compiler calculates buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24) as an int only.

If you give a hint to the compiler by telling it you want long expressions used, then it should work. The easiest way to do so is to replace the number of bits to be shifted as long literals:

long  l = buf[0] | (buf[1] << 8L) | (buf[2] << 16L) | (buf[3] << 24L);

That should be enough for the compiler to perform the whole operation as long and not loose precision.

  • Sorry, even with the implicit long, the result is -26371.
    – Blitz
    Dec 10 '14 at 19:00

Just for completeness, after posting this, I've found another way around it, but @Phataas solution is much more elegant. Using the ltoa and atol functions, long to char array and vice-verse can be done like this:

char temp[10];
long result = atol(temp);
  • It is also a lot more inefficient. Dec 10 '14 at 19:22
  • Yes, it is - but if you're goal is to write a text file to SD card, it beats the binary approach. I've ended up using a combination of the byte conversion with union and the ltoa version for printing to log files
    – Blitz
    Dec 10 '14 at 23:35
char text[5];
long number = 1234;
  • 2
    A long can take more than 5 decimal digits, and should be converted using the "%ld" format. Jul 10 '20 at 11:20
  • Please expand upon your answer and give it an explanation why you believe your code is correct. Aug 1 '20 at 16:47

Not the answer you're looking for? Browse other questions tagged or ask your own question.