Teekeks
Teekeks

Reputation: 196

store 2 signed shorts in one unsigned int

This is given:

signed short a, b;
a = -16;
b = 340;

Now I want to store these 2 signed shorts in one unsigned int and later retrieve these 2 signed shorts again. I tried this but the resulting shorts are not the same:

unsigned int c = a << 16 | b;

signed short ar, br;
ar = c >> 16;
br = c & 0xFFFF;

Upvotes: 3

Views: 403

Answers (5)

RoGK&#244;T
RoGK&#244;T

Reputation: 29

Not sure if this way of doing is good for portability or others but I use...

#ifndef STDIO_H
#define STDIO_H
#include <stdio.h>
#endif

#ifndef SDTINT_H
#define STDINT_H
#include <stdint.h>
#endif

#ifndef BOOLEAN_TE
#define BOOLEAN_TE
typedef enum {false, true} bool;
#endif  

#ifndef UINT32_WIDTH
#define UINT32_WIDTH 32 // defined in stdint.h, inttypes.h even in libc.h... undefined ??
#endif

typedef struct{
struct{                 // anonymous struct
    uint32_t    x;
    uint32_t    y;
};}ts_point;

typedef struct{
struct{                 // anonymous struct
    uint32_t    line;
    uint32_t    column;
};}ts_position;

bool    is_little_endian()
{
    uint8_t n = 1;
    return *(char *)&n == 1;
}

int main(void)
{
    uint32_t    x, y;
    uint64_t    packed;
    ts_point    *point;
    ts_position *position;

    x = -12;
    y = 3457254;
    printf("at start: x = %i | y = %i\n", x, y);
    if (is_little_endian()){
        packed = (uint64_t)y << UINT32_WIDTH | (uint64_t)x;
    }else{
        packed = (uint64_t)x << UINT32_WIDTH | (uint64_t)y;
    }
    printf("packed: position = %llu\n", packed);
    point = (ts_point*)&packed;
    printf("unpacked: x = %i | y = %i\n", point->x, point->y); // access via pointer
    position = (ts_position*)&packed;
    printf("unpacked: line = %i | column = %i\n", position->line, position->column);
    return 0;
}

I like the way I do as it's offer lots of readiness and can be applied in manay ways ie. 02x32, 04x16, 08x08, etc. I'm new at C so feel free to critic my code and way of doing... thanks

Upvotes: -1

R Sahu
R Sahu

Reputation: 206717

Since a has a negative value,

unsigned int c = a << 16 | b;

results in undefined behavior.

From the C99 standard (emphasis mine):

6.5.7 Bitwise shift operators

4 The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1 x 2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1 x 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.


You can explicitly cast the signed short to unsigned short to get a predictable behavior.

#include <stdio.h>

int main()
{
   signed short a, b;
   a = -16;
   b = 340;

   unsigned int c = (unsigned short)a << 16 | (unsigned short)b;

   signed short ar, br;
   ar = c >> 16;
   br = c & 0xFFFF;

   printf("ar: %hd, br: %hd\n", ar, br);
}

Output:

ar: -16, br: 340

Upvotes: 1

chux
chux

Reputation: 154169

OP almost had it right

#include <assert.h>
#include <limits.h>

unsigned ab_to_c(signed short a, signed short b) {
  assert(SHRT_MAX == 32767);
  assert(UINT_MAX == 4294967295);
  // unsigned int c = a << 16 | b; fails as `b` get sign extended before the `|`.
  // *1u insures the shift of `a` is done as `unsigned` to avoid UB 
  //    of shifting into the sign bit.
  unsigned c = (a*1u << 16) | (b & 0xFFFF);
  return c;
}

void c_to_ab(unsigned c, signed short *a, signed short *b) {
  *a = c >> 16;
  *b = c & 0xFFFF;
}

Upvotes: 2

DrPrItay
DrPrItay

Reputation: 838

This is really weird, I've compiled your code and it works for me perhaps this is undefined behavior I'm not sure, however if I were you I'd add castings to explicitly avoid some bit loss that may or may not be caused by abusing two complement or compiler auto casting....

In my opinion what's happening is probably you shifting out all the bits in a... try this

unsigned int c = ((unsigned int) a) << 16 | b;

Upvotes: 0

David Cimbalista
David Cimbalista

Reputation: 15

This is because you are using an unsigned int, which is usually 32 bits and a negative signed short which is usually 16 bits. When you put a short with a negative value into an unsigned int, that "negative" bit is going to be interpreted as part of a positive number. And so you get a vastly different number in the unsigned int.

Storing two positive numbers would solve this problem....but you might need to store a negative one.

Upvotes: -1

Related Questions