2

Say I have the following:

int32 a = ...; // value of variable irrelevant; can be negative
unsigned char *buf = malloc(4); /* assuming octet bytes, this is just big 
                          enough to hold an int32 */

Is there an efficient and portable algorithm to write the two's complement big-endian representation of a to the 4-byte buffer buf in a portable way? That is, regardless of how the machine we're running represents integers internally, how can I efficiently write the two's complement representation of a to the buffer?

This is a C question so you can rely on the C standard to determine if your answer meets the portability requirement.

7
  • use int instead of int32. I believe that int is 2's compliment. Also, the endianess only matters when you talk to another computer passing in the interger data. if that's an issue, then you have to do an endianess check on that machine to see if it's compatable. do that by passin in the char 1 and have them send you the int value for 1. does this make sense? Commented Aug 15, 2013 at 4:08
  • @PaulNikonowicz: The C standard does not require that int be two's complement. The endianness does matter in this case because we're talking about writing an integer to a character buffer, and we don't want the number to be written "backwards". Commented Aug 15, 2013 at 4:17
  • a char buffer would eliminate your endianess problem. Commented Aug 15, 2013 at 4:18
  • 2
    @PaulNikonowicz, what does that mean? If you have a multi-byte type, you have to pick one endianness or another. Commented Aug 15, 2013 at 4:32
  • 1
    We're writing the representation of a 4-byte integer to a 4-byte array so endianness is obviously an issue. Commented Aug 15, 2013 at 4:47

3 Answers 3

2

Yes, you can certainly do it portably:

int32_t a = ...;
uint32_t b = a;
unsigned char *buf = malloc(sizeof a);

uint32_t mask = (1U << CHAR_BIT) - 1;  // one-byte mask

for (int i = 0; i < sizeof a; i++)
{
    int shift = CHAR_BIT * (sizeof a - i - 1); // downshift amount to put next
                                               // byte in low bits
    buf[i] = (b >> shift) & mask;  // save current byte to buffer
}

At least, I think that's right. I'll make a quick test.

Sign up to request clarification or add additional context in comments.

6 Comments

You're making it too hard. "Convert to twos complement" is simply the operation of conversion to an unsigned type that can cover the full range.
That's true; that would clean up a few lines. The "hard part" is the last bit in the loop.
Edited; it works now in a couple of tests here. I'll break up the last bit to make it clearer what's going on.
Upon more staring at this solution, I think it depends on sizeof a being greater than 1. Otherwise the mask will break... you can work around that if it's a problem, I guess.
@R.. That's very interesting if it is indeed portably true according to the C standard. I can't quite wrap my head around the reasoning yet, though. Thanks for your and Carl's help. Just wondering, is there an analogous strategy for performing the opposite operation? In other words, can we "parse" a two's complement integer from memory manually by reading it into an unsigned variable and converting it to a signed variable of the same size?
|
2
unsigned long tmp = a; // Converts to "twos complement"
unsigned char *buf = malloc(4);
buf[0] = tmp>>24 & 255;
buf[1] = tmp>>16 & 255;
buf[2] = tmp>>8 & 255;
buf[3] = tmp & 255;

You can drop the & 255 parts if you're assuming CHAR_BIT == 8.

1 Comment

Cast to unsigned long was a really good idea -- made it much simpler. (+1)
0

If I understand correctly, you want to store 4 bytes of an int32 inside a char buffer, in a specific order(e.g. lower byte first), regardless of how int32 is represented.

Let's first make clear about those assumptions: sizeof(char)=8, two's compliment, and sizeof(int32)=4.

No, there is NO portable way in your code because you are trying to convert it to char instead of unsigned char. Storing a byte in char is implementation defined.

But if you store it in an unsigned char array, there are portable ways. You can right shift the value each time by 8 bit, to form a byte in the resulting array, or with the bitwise and operator &:

// a is unsigned
1st byte = a & 0xFF
2nd byte = a>>8 & 0xFF
3rd byte = a>>16 & 0xFF
4th byte = a>>24 & 0xFF

4 Comments

Actually implementation-defined, not undefined.
if char is an unsigned type, it's fine.
@R.. Correct. Updated.
Yes, I want to work off the assumption that sizeof(int32) == 4 and CHAR_BIT == 8. The char thing was a typo, I meant unsigned char.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.