Re: Two's Complement, Serialization, etc.



Beej Jorgensen wrote:
Here's what I'm trying to do: portably serialize signed 16-bit integers.
I'd like some ideas for improvement here.

1 void pack16(unsigned char buf[2], int16_t v)
2 {
3 uint16_t v2 = v;
4 buf[0] = v2 >> 8;
5 buf[1] = v2;
6 }

7 int16_t unpack16(unsigned char buf[2])
8 {
9 uint16_t v2 = buf[0] << 8 | buf[1];
10
11 if (v2 > 0x7fffu) { // check for negative number
12 return -(0xffffu - v2 + 1u);
13 }
14
15 return v2;
16 }

Comments/Questions:

1. I believe the direct assignment to the unsigned variable in pack16()
line 3 should give us the two's complement negative by C99-6.3.1.3p2.

In truth, it can't give a negative of any description, since
an unsigned type cannot express a negative value. However, you're
right that the bit pattern in `v2' will be the same that `v' would
have if `v' used two's complement (whether it actually does or not).

2. By the same paragraph, I think "&0xff"s on lines 4 and 5 are
unnecessary.

Unnecessary in line 4, but required in line 5 if CHAR_BIT > 8.

3. Are casts required on line 12 for any of the constants? I'm thinking
the answer is "no" by C99-6.4.4.1p5.

I see no reason for casts on any of the constants. The only
constants in play are 0, 1, 8, 0x7fffu, 0xffffu, and 1u, all of
which already have the types you want. (The constant 2 is "not
really there;" it's for documentation only. C99 has a new use
of `static' that could make the 2 a bit solider.)

4. Are casts required on the return values? Or anywhere on the
expression on line 12? I'm thinking no, but can't find a passage to
cite.

In line 12 there are two cases: Either the arithmetic is done
in `int' (if INT_MAX >= 65535) or in `unsigned int'. If it's done
in `int', everything looks all right as it stands: Neither the
subtraction nor the addition nor the negation will overflow or
wrap or do anything surprising, and you'll wind up with a negative
`int' that's in range for an `int16_t'. If `unsigned int' is used,
though, I think you're on shakier ground. Consider v2 == 0x8000u,
for example: The subtraction gives 0x7fffu, then the addition gives
0x8000u, this is unchanged by negation, and now you try to convert
the value 0x8000 == 32768 to `int16_t', a type that only goes up
to 32767. Implementation-defined result (you're hoping for -32768,
but it's not guaranteed) or implementation-defined signal.

I think `return -1 - (int16_t)(0xffffu - v2);' will do the trick,
but I haven't plodded carefully through all the cases.

5. In unpack16(), I cannot portably assign the (buf[0]<<8)|buf[1]) into an
int16_t as it might overflow (C99-6.3.1.3p3.) And in pack16(), I
cannot portably assign v>>8 into buf[0] if v is negative
(C99-6.3.1.3p4.)

6. In the code, I assume the implementation supports int16_t and
uint16_t, which it is not required to do. But I don't see why the
above code shouldn't work with the "least16" variants.

The only problem I can see with the `least16' versions is that
you might need to add out-of-range detection in pack16(). (You might
receive a `v' that is in range for a 24-bit `int_least16_t' but
outside the range you can encode in two eight-bit bytes.)

7. Endianess issues? sizeof-related issues? sizeof-related issues if
least16-type variables were used? CHAR_BIT-related issues? Integer
promotion-related issues?

As noted above.

8. Any way to take advantage of the fact that int16_t is two's
complement in unpack16()?

I don't think so. We know that `int16_t' uses two's complement
representation, but we don't know how conversion to `int16_t' deals
with out-of-range values. Once you get there, you're safe -- but
watch out for that first step!

Anything else? Something feels wrong or oversimplified or
overcomplicated or lame.

You're exercising a lot of care (almost enough :-), which may
or may not be justified depending on the environment.

Portable representations of binary data have received a lot of
study, and you might consider adopting an existing solution instead
of rolling your own freshly-smoothed wheel ... Question 20.5 in
the comp.lang.c FAQ <http://c-faq.com/index.html> might be a good
starting point.

--
Eric.Sosman@xxxxxxx
.



Relevant Pages

  • Re: Do you use any computer based tool for doing project layout?
    ... Above you use 2's complement representations in your example. ... takes zero clock cycles to invert a signal), but this is a negation ...
    (rec.woodworking)
  • Re: unary negation operator question
    ... > unsigned short int abs_val; ... > I take it that the above is how I should safely get the absolute value ... wraparound trickery to get to the negation of the original value. ...
    (comp.lang.c)
  • Re: Do you use any computer based tool for doing project layout?
    ... Only if you're doing invert-add to perform subtraction. ... Above you use 2's complement representations in your example. ... takes zero clock cycles to invert a signal), but this is a negation ...
    (rec.woodworking)
  • Re: decimal constant too large
    ... int main ... Produces the following warning: ... the unary negation operator is applied to a constant. ... this large unsigned number provokes, technically, integer overflow. ...
    (comp.lang.c)
  • Re: unary operator- question
    ... I'm guessing there must be some simple code that will return the ... > negative of a value using the unary operator - ... int negative_two = -two; ... But if you're trying to write the negation operator for some class, ...
    (comp.lang.cpp)