2

I have a variable that have 2x int16 values as:

int32_t value = 0x1234ABCD; // a=0x00001234, b=0xFFFFABCD

The naive solution is to do a mask:

int32_t a = (value & 0xFFFF0000) >> 16;
int32_t b = (value & 0x0000FFFF);

But with this, I don't have any sign expansion and b becomes 0x0000ABCD instead of 0xFFFFABCD.

My next attempt was to use an intermediate structure

struct dual_int16 {
   long hi:16;
   long lo:16;
}

int32_t a = (struct dual_int16)value).lo;
int32_t a = (struct dual_int16)value).hi;

Unfortunately my compiler doesn't allow me to do this "struct dual_int16 is not allowed" or "type of cast must be arithmetic or pointer".

Is there any correct way to extract my 2x int16 with sign expansion in C99?

EDIT

Because I am using a specific compiler (ADSP-21xxx) . I don't have all the standard types defined in stdint.h such as int16_t. My compiler does not recognize int8_t and int16_t.

The arch has an hybrid 32-48bits dual ALU, big endian.

nowox
  • 25,978
  • 39
  • 143
  • 293

3 Answers3

3

If you want signed 16-bit values, use the proper type:

#include <stdint.h>

const int32_t value = 0x1234ABCD; // a=0x00001234, b=0xFFFFABCD

const int16_t a = (value >> 16) & 0xffff;
const int16_t b = value & 0xffff;

printf("a=%hd\nb=%hd\n", (short) a, (short) b);

This prints:

a=4660
b=-21555

Also note that I shift before I mask, to reduce the literal size of the masks. This is probably pointless with modern smart optimizing compilers, but that's the reason I changed it.

I used int16_t since you used uint32_t and mentioned C99, that really made me believe you should have it. Make sure you #include <stdint.h>.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • If OP needs to store them into `int32_t`, I guess you could implicitly cast `int16_t` to `int32_t` by `int32_t a = (uint16_t)((value >> 16) & 0xffff);`. – bzeaman Dec 10 '14 at 15:10
  • I really don't get the `(uint16_t)` cast there, that's not what you said you wanted to do ... I'm quite skeptical to this suggestion. – unwind Dec 10 '14 at 15:22
  • I'm terribly sorry. I meant `int16_t`. I think you confused me by mentioning `uint32_t` in your answer. Oh dear. – bzeaman Dec 10 '14 at 15:24
  • Regarding reducing the literal size, you wouldn't want to do `(value & 0xFFFFFFFF) >> 16` anyhow, because right-shifting a negative integer is implementation defined behavior: you don't know whether it will fill up with zeroes (logical shift) or with the sign bit (arithmetic shift). The code you have written avoids this issue since you mask away the unwanted bits _after_ the shift. – Lundin Dec 10 '14 at 15:27
  • @Lundin That's true, and perhaps another reason I switched to this pattern a long time ago. :) Thanks. – unwind Dec 10 '14 at 15:28
  • Oh and btw... nobody expects implicit integer promotion! A hex literal larger than 0x7fff is actually of unsigned type on a 16 bit system. This means that `value & 0xffff` will get promoted to an unsigned type, since 0xffff is then actually of type unsigned int. This could potentially cause a bug, but as far as I can see this particular code will be okay. Mixing signed integers with bitwise operators is always dangerous. – Lundin Dec 10 '14 at 15:41
1

union will do the trick.

union {
    int32_t value;
    struct {
        int16_t v1, v2;
    } decomp;
} extract;

/* ... */
extract.value = value;
int32_t a = extract.decomp.v1, b = extract.decomp.v2;

notice that, a and b have desired signs.

Jason Hu
  • 6,239
  • 1
  • 20
  • 41
  • 2
    Might work, but more work and more error-prone than the manual masking and shifting. Not all the world is little-endian. – Deduplicator Dec 10 '14 at 14:49
  • 1
    @Deduplicator yeah. actually in big endian, it will do the trick too. but `v1` and `v2` are in reversed order. not necessary to be more error-prone than manual shift, since they are actually the same. – Jason Hu Dec 10 '14 at 14:52
  • Never read a member that has been written to by another member. – bzeaman Dec 10 '14 at 14:52
  • You're only supposed to read the field which was most recently written. – bzeaman Dec 10 '14 at 14:58
  • Sorry, I can't find it in the standard at the moment. But I have [an answer that backs me up](http://stackoverflow.com/a/252568/2703418). – bzeaman Dec 10 '14 at 15:01
  • @BennoZeeman not in c99 anymore, because value touch the whole union you are allowed to read from v1 and v2. Reading value after writing to v1 is not allowed. – mch Dec 10 '14 at 15:01
  • @BennoZeeman decomposing bit/byte sequence using `union` is a regular trick in C, heavily used in low layer programming. – Jason Hu Dec 10 '14 at 15:03
  • 1
    C99, 6.2.6.1: "When a value is stored in a member of an object of union type, the bytes of the object representation that do not correspond to that member but do correspond to other members take unspecified values.". I'm a bit lost now, but I'm pretty sure people should avoid it if they can. Low layer programming is different, because usually you're targeting a specific architecture etc. – bzeaman Dec 10 '14 at 15:06
  • @BennoZeeman yeah. sometimes people will avoid that, if you are defining a `union` to represent a selection. that is something like `struct { enum myenum choice; union { /* some structs */ } value; }`. but in my case, it has to work in this way. not necessary to target a specific arch, if you are decomposing a stream in TCP/IP, you will be happy to use `union` too. – Jason Hu Dec 10 '14 at 15:14
-2
short a = (short)( (value >> 16) & 0xFFFF );
short b = (short)( value & 0xFFFF );

int32_t is not a standard type, as well (comment to @coin). short/short int is equivalent of int16_t.

i486
  • 6,491
  • 4
  • 24
  • 41
  • What do you mean by int32_t is not a standard type? sizeof( short ) could be > 2. – 2501 Dec 10 '14 at 14:46
  • Might be, or might not be equivalent. And it's a *standard*-typedef, if the implementation has a fitting type, in C99 (and probably back-ported). – Deduplicator Dec 10 '14 at 14:46
  • It is comment from above: "Unfortunately int16_t doesn't exists with my compiler". For that reason I say that int32_t is also not a normal type. – i486 Dec 10 '14 at 14:48