0

I was looking at a problem from cs61c (ucb). I have the following method:

void lfsr_calculate(uint16_t *reg) {                                      
  uint16_t result = compute_bit_val(*reg);                              
  printf("reg value: %d", *reg);                                        
  printf("bit val result: %d", result);                                 
  printf("bit val result shifted: %d", result << 16);                   
  *reg >>= 1;                                                           
  printf("bit val result shifted plus zero: %d", *reg + (result << 16));
  *reg = (uint16_t) *reg + (result << 16);                              
  printf("new reg: %d", *reg);                                          
}

If *reg is 1, my method compute_bit_val returns 1. The print output is

1 

1

65536

65536

**0**

?!?!?! I am pulling out my hair, I don't know why the last part is zero, for some reason the assignment is not working. I tried this with and without casting, and it gives the same result.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
Andrew
  • 6,295
  • 11
  • 56
  • 95
  • 1
    Not directly related, but you should make `result` be `uint32_t`. As you have it, it causes undefined behaviour if `result` had its high bit set because unfortunately, when `int` is 32-bit, the integer promotions take `uint16_t` to `int` (not `unsigned int`). – M.M Mar 05 '15 at 04:21
  • Nobody can give a complete answer unless you state which CPU you are using. This code will probably work on 8 and 16 bits CPUs but fail at larger ones. Also, printing unsigned integers as if they were signed is probably not a good idea. Read up on printf format specifiers. – Lundin Mar 05 '15 at 08:29

1 Answers1

6

In the last step, you assign 65536 to *reg which is uint16_t. However uint16_t can only store values from 0 to 65535, so it gets adjusted via modular arithmetic to have value 0. (aka. wraps around).

You may be overlooking that integer arithmetic is always performed in at least int precision; narrower types are immediately promoted to int before computing the result of any arithmetic operator.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • "You may be overlooking that integer arithmetic is always performed in at least int precision [...]" No it's not. The promotion happens in this case because the right-hand side of the shift operator is an int. – Pseudonym Mar 05 '15 at 04:43
  • 2
    @Pseudonym the type of the right-hand side of the shift operator makes no difference. The left operand undergoes the integer promotions (but does not try to find a common type with the right operand, as happens for `+` for example). – M.M Mar 05 '15 at 04:47
  • Bleah, you're right. It's too late in the day for me. – Pseudonym Mar 05 '15 at 04:51
  • Shouldn't he be getting a warning about assigning to a uint16_t from an int without a cast though? The cast on *reg does nothing here. – jschultz410 Mar 05 '15 at 06:14
  • @jschultz410 there are no required diagnostics relating to narrowing conversions of integers. Indeed, the cast does nothing. – M.M Mar 05 '15 at 06:17
  • `uint16_t result` ... `result << 16` never makes any sense. The shift operators don't use regular integer promotion, but the result will always be in the type of the left operand. Anyway, if you write bit-wise operations that rely on implicit integer promotions, you likely don't have a clue about what your own code is doing anyhow... – Lundin Mar 05 '15 at 08:34
  • @Lundin `result << 16` makes some sense, if `int` is 32-bit: it'll move those 16 bits into the upper 16 bits of the `int`. Agree that this code should be tightened up to be portable. – M.M Mar 05 '15 at 09:41
  • @MattMcNabb It never makes any sense to shift a uint16_t 16 bits. – Lundin Mar 05 '15 at 12:36
  • I meant to be shifting by 15, instead of 16. I don't know why I didn't see my error, thanks. – Andrew Mar 05 '15 at 17:12
  • Why did it print the value, instead of overflowing earlier? – Andrew Mar 05 '15 at 17:54
  • @Andrew sorry I don't understand the question. What output were you expecting? – M.M Mar 05 '15 at 22:47
  • @McNabb I should have shifted by 15; I forgot that 16 was enough to overflow it when starting at the first bit. I was curious why the printf displays the actual number, instead of the overflowed to 0, when the number in the printf (*reg + (result << 16)) should also be a uint_16. It seems that printf doesn't overflow whereas the pointer dereference does, even though they are the same type. – Andrew Mar 05 '15 at 23:35
  • @Andrew as mentioned in my answer - all arithmetic is performed in at least `int` precision. When you write `result << 16` it is the same as `((int)result) << 16`. In technical terms, any values of type narrower than `int` are converted to `int` before applying an arithmetic operator. – M.M Mar 06 '15 at 00:41