portable and efficient signed bitfields

A

Andrew Oakley

I'm trying to fix up some code that uses a signed integer to store
various formed of packed data. Signed integer values are sometimes
present in the top 29 bits. The current code does:

signed int data;
data = ... | value << 3; // store value in top bits
value = data >> 3; //get value out of top bits

This is somewhat broken as shifting of negative numbers is
implementation defined. It also gets very hard to read.

I've got some new code that looks more like this:

static unsigned int store_signed(signed int i, unsigned char len) {
return (i < 0) ? (1 << len) - ((unsigned int)-i) : i;
}
static signed int load_signed(unsigned int i, unsigned char len) {
return (i & ((1 << (len - 1)) - 1)) - (i & (1 << (len - 1)));
}
struct {
unsigned int something_else:3;
unsigned int value:29;
} data;
data.something_else = ...
data.value = store_signed(value, 29);
value = load_signed(data.value, 29);

Using bitfields I can get rid of the excessive bitwise manipulations
happening in the program, which should make it much clearer. The load
and store routines turn the signed values into unsigned values so that
they can be stored in the bitfields (behaviour with signed bitfield
values is also implementation defined).

The store_signed routine is fast - GCC removes the lot, and we end up
with the same code as before, but I can't get the load_signed one to
generate the nice compact code that I want - a single sar (shift
arithmetic right) to do the unpacking and sign extension.

Any ideas would be appreciated - the only real constraint is that I
have to keep the same binary format as before.
 
B

Ben Bacarisse

Andrew Oakley said:
I'm trying to fix up some code that uses a signed integer to store
various formed of packed data. Signed integer values are sometimes
present in the top 29 bits.
I've got some new code that looks more like this:

static unsigned int store_signed(signed int i, unsigned char len) {
return (i < 0) ? (1 << len) - ((unsigned int)-i) : i;
}
static signed int load_signed(unsigned int i, unsigned char len) {
return (i & ((1 << (len - 1)) - 1)) - (i & (1 << (len - 1)));
}

The "usual" code is:

static signed int load_signed(int i, unsigned char len) {
int mask = 1 << (len - 1);
return (i ^ mask) - mask;
}

This is taken from [1]. Obviously you can simplify this since you
have only two cases when len==29 and len==3. You could write load_top
and load_bottom that have a constant mask built-in.
struct {
unsigned int something_else:3;
unsigned int value:29;
} data;
data.something_else = ...
data.value = store_signed(value, 29);
value = load_signed(data.value, 29);

Using bitfields I can get rid of the excessive bitwise manipulations
happening in the program, which should make it much clearer. The load
and store routines turn the signed values into unsigned values so that
they can be stored in the bitfields (behaviour with signed bitfield
values is also implementation defined).

How so? In the bad old days, you could not rely on signed int
bit-fields but I think you can now (i.e. since the 89 standard).
The store_signed routine is fast - GCC removes the lot, and we end up
with the same code as before, but I can't get the load_signed one to
generate the nice compact code that I want - a single sar (shift
arithmetic right) to do the unpacking and sign extension.

Any ideas would be appreciated - the only real constraint is that I
have to keep the same binary format as before.

Signed bit-fields win for me, but I have no idea what code you'll get.

[1] http://graphics.stanford.edu/~seander/bithacks.html.
 
A

Andrew Oakley

The "usual" code is:

static signed int load_signed(int i, unsigned char len) {
int mask = 1 << (len - 1);
return (i ^ mask) - mask;
}
I think thats much clearer, thanks.

How so? In the bad old days, you could not rely on signed int
bit-fields but I think you can now (i.e. since the 89 standard).

I think i must have just got confused by the spec:
A bit-field may have type int , unsigned int , or signed int .
Whether the high-order bit position of a ``plain'' int bit-field is
treated as a sign bit is implementation-defined. A bit-field is
interpreted as an integral type consisting of the specified number of
bits.

I guess that just means if you do "int" instead of "signed int" it is
implementation defined, not that the whole thing is implementation
defined.

Signed bit-fields win for me, but I have no idea what code you'll get.
Yes, having read the spec more carefully I would agree, and the
generated code is pretty elegant in GCC.


Thanks for the help.
 
P

Phil Carmody

Ben Bacarisse said:
The "usual" code is:

static signed int load_signed(int i, unsigned char len) {
int mask = 1 << (len - 1);
return (i ^ mask) - mask;
}

The ^ is IDB or UB according to 6.5 (4) and then the - may invoke
UB according to 6.5 (5), surely?

Phil
 
H

Harald van Dijk

Ben Bacarisse said:
[...]
The "usual" code is:

static signed int load_signed(int i, unsigned char len) {
int mask = 1 << (len - 1);
return (i ^ mask) - mask;
}

The ^ is IDB or UB according to 6.5 (4) and then the - may invoke UB
according to 6.5 (5), surely?

Depending on the function arguments, this function may have
implementation-defined or undefined behaviour, but only if the function
arguments are incorrect for how the function is supposed to be used.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top