0

Say I have the following code:

uint32_t fillThisNum(int16_t a, int16_t b, int16_t c){ uint32_t x = 0; uint16_t temp_a = 0, temp_b = 0, temp_c = 0; temp_a = a << 24; temp_b = b << 4; temp_c = c << 4; x = temp_a|temp_b|temp_c; return x; } 

Essentially what I'm trying to do is fill the 32-bit number with bit information that I can extract at a later time to perform different operations.

Parameter a would hold the first 24 bits of "data", b would hold the next 4 bits of "data" and c would hold the final 4 bits of "data".

I have a couple questions:

  1. Do the parameters have to be the same bit length as the function type, and must they be unsigned?
  2. Can I assign an unsigned int to a signed int? (i.e. uint32_t a = int32_t b;)
  3. Can I fill a 32-bit number with the 16-bit parameters so long they don't exceed the length of the 32-bit return value.

Any advice/tips/hints would be much appreciated, thank you.

8
  • 2
    Note: if int bit size is 16, temp_a = a << 24; is not well defined. Commented Oct 9, 2014 at 19:18
  • 1
    Usually better to do shifting with unsigned types vs. signed ones. Commented Oct 9, 2014 at 19:19
  • When you say "Do the parameters have to be the same bit length as the function type...", by "function type", do you mean the type of the return value of the function? Commented Oct 9, 2014 at 19:32
  • @plafratt Yes, if the return type is uint32_t, do the parameters have to be of type uint32_t as well. Commented Oct 9, 2014 at 19:33
  • Can you describe which bits of the 32-bit result are supposed to come from where? Your actual code only fills in (at most) the lower 16 bits of x, since all of the temps are 16-bit, and it overlaps b with c. Commented Oct 9, 2014 at 22:03

2 Answers 2

1

A correct way to write this code is:

uint32_t fillThisNum(uint32_t a, uint32_t b, uint32_t c) { // mask out the bits we are not interested in a &= 0xFFFFFF; // save lowest 24 bits b &= 0xF; // save lowest 4 bits c &= 0xF; // save lowest 4 bits // arrange a,b,c within a 32-bit unit so that they do not overlap return (a << 8) + (b << 4) + c; } 

By using an unsigned type for the parameters, you avoid any issues with signed arithmetic overflow, sign extension, etc.

It's OK to pass signed values as arguments when calling the function, those values will be converted to unsigned.

By using uint32_t as the parameter type then you avoid having to declare any temporary variables or worry about type width when doing your casting. It makes it easier for you to write clear code, this way.

You don't have to do it this way but this is a simple way to make sure you don't make any mistakes.

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

2 Comments

Very good yet simple explanation Matt. Needed a lead in the right direction and this helped. I can apply this logic to a much more complex problem now, thanks!
As I alluded to in my response, if you need to send 16-bit arguments, you can do so and then move them to 32-bit temps to set the appropriate bits in the return value.
1
  1. Do the parameters have to be the same bit length as the function type, and must they be unsigned?

No, the arguments and the return value can be different types.

  1. Can I assign an unsigned int to a signed int? (i.e. uint32_t a = int32_t b;)

Yes, the value will be converted from a signed to an unsigned value. The bits in "b" will stay the same, so while "b" is in 2's complement, "a" will be a positive 32-bit number.

So, for example, let int8_t c = -127. If you perform an assignment uint8_t d = c, then "d" will be 129.

  1. Can I fill a 32-bit number with the 16-bit parameters so long they don't exceed the length of the 32-bit return value.

If by that, you mean the way that you did in your code:

x = temp_a|temp_b|temp_c; 

Yes, that is fine, with the caveat that @chux mentioned: you can't shift an n-bit value more than n bits. If you wanted to set bits more significant than bit 15 in x, a way to do this would be to set up one of the temp masks with a 32-bit value instead of a 16-bit one.

6 Comments

The code int8_t c = 0x81; could give any value to c or raise a signal (and must be described by the compiler documentation). This is implementation-defined behaviour.
That's interesting. Is that based on the C specification? If so, could you please provide a reference? Can I instead say uint8_t c = -127?
Yes, C99 6.3.1.3/3 . uint8_t c = -127; is well-defined and is covered by 6.3.1.3/2.
Ok, thanks. I did not know that. I figured the compiler did the same thing for both literals in all cases. I hope to have a look at the standard.
In C11 it is the same numbering and text. Note that 0x81 is a literal of type int and value 129.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.