5

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?

9

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:

myUnion.myLong;

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:

myUnion.myByte[0];
myUnion.myByte[1];
myUnion.myByte[2];
myUnion.myByte[3];
| improve this answer | |
  • 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 – iQt Dec 10 '14 at 19:20
1

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.

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

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];
ltoa(669L,temp,10);
long result = atol(temp);
| improve this answer | |
  • It is also a lot more inefficient. – iQt 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
-1
char text[5];
long number = 1234;
sprintf(text,"%d",number);
| improve this answer | |
  • 2
    A long can take more than 5 decimal digits, and should be converted using the "%ld" format. – DataFiddler Jul 10 at 11:20
  • Please expand upon your answer and give it an explanation why you believe your code is correct. – Greenonline Aug 1 at 16:47

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