Bitshifting bytes to form a long var fails!

Hi there,

i am using Arduino 1.0 with UNO board!
I am writing some new code and i am using bitshift to assign 5 bytes to a long var.
The code is:

address = a<<16 | b<<12 | c<<8 | d<<4 | e;

address is the long var and the a, b, c, d ,e are the bytes!
Everything is ok except the a<<16. This does nothing to the long var!
I wrote a test function where i "moved" the a byte from the low nibble to the high nibble of the long.
When it reaches the a<<16 then the long is 0x00000000 instead of 0x000F0000!

Is this a bug or am i doing something wrong?

Thank you in advance!
Padelis

Use brackets - they are free.

Is this a bug or am i doing something wrong?

Everything is ok except the a<<16

What happens if a is cast to a long?

By the way, for this kind of shifts, you can execute them a lot faster with unions or pointers.

A four bit shift is tricky with a union or a pointer, but I'm willing to learn, dhenry.

The same happens if i use brackets and if a is cast to a long! This is really weird!
dhenry what do you mean unions and pointers? An example maybe would help!

Thank you guys

The same happens if i use brackets and if a is cast to a long!

Your code and your output would be useful.

I wouldn't hold my breath waiting for dhenry's four bit shift union/pointer code.

psyche:
a, b, c, d ,e are the bytes!
Everything is ok except the a<<16. This does nothing to the long var!

What do you expect? A byte is 8 bits. You shift it left 16 bits. What is left? Zero.

Using union or pointers to reconstitute a different data type is actually fairly common.

The solution I will provide you is built on that and takes just two instruction cycles to shift any number of bits (less than 8 obvious as you can break any shifts down). It is a hardware based solution available to any avr.

I will let you guys think about it for a while though.

Btw, if you are using those lower-end avrs, you have a hardware 4 / 12-bit shifter.

dhenry:
The solution I will provide you is built on that and takes just two instruction cycles to shift any number of bits (less than 8 obvious as you can break any shifts down). It is a hardware based solution available to any avr.

Well, let's see it.

The compiler generated this reasonably-efficient code to shift left 4 bits:

  fc:	99 2d       	mov	r25, r9
  fe:	92 95       	swap	r25
 100:	90 7f       	andi	r25, 0xF0	; 240

That's 3 cycles.

byte a = 0x0F;

So it shouldn't be zero!
Here is some code:

   for (byte a = str5; a <= 0x0F; a++) {
    for (byte b = str4; b <= 0x0F; b++) {
      for (byte c = str3; c <= 0x0F; c++) {
        for (byte d = str2; d <= 0x0F; d++) {
          for (byte e = str1; e <= 0x0F; e+=2) {
            counter++;            
            address = a<<16 | b<<12 | c<<8 | d<<4 | e;
            sprintf(result, "%04X",(char*) GetWord((long)address));
            //Serial.print(result);
            Serial.print(address, HEX);
            if (counter == ((Stop - Start)/2)) goto out;
          }
          Serial.println();
        }
      }
    }
  }

Its more subtle than that - by default an expression is promoted to the widest type present in it.
Here the constant 16 is of type int and is thus 16 bits on the Arduino. An expression of the form
byte << int widens its result to int. Thus the shift is done as a 16 bit shift after the byte 'a' is widened
to 16 bits. (If 'a' was of type char (which is signed) the widening would propagate the sign into the top
8 bits of the 16 bit value before the shift). However a shift of 16 still returns zero of course and the cast is needed:

address = ((long)a)<<16 | b<<12 | c<<8 | d<<4 | e;

(Assuming 'b' only contains 4 useful bits)

MarkT:

[quote author=Nick Gammon link=topic=127079.msg955678#msg955678 date=1350165839]

psyche:
a, b, c, d ,e are the bytes!
Everything is ok except the a<<16. This does nothing to the long var!

What do you expect? A byte is 8 bits. You shift it left 16 bits. What is left? Zero.

Its more subtle than that - by default an expression is promoted to the widest type present in it.
Here the constant 16 is of type int and is thus 16 bits on the Arduino. An expression of the form
byte << int widens its result to int. Thus the shift is done as a 16 bit shift after the byte 'a' is widened
to 16 bits. (If 'a' was of type char (which is signed) the widening would propagate the sign into the top
8 bits of the 16 bit value before the shift). However a shift of 16 still returns zero of course and the cast is needed:

address = ((long)a)<<16 | b<<12 | c<<8 | d<<4 | e;

(Assuming 'b' only contains 4 useful bits)
[/quote]

I tried that too but it didn't work! I also updated to 1.0.1 but still no luck.
The only method that works is with union! I have managed to make it work that way! Thank you all and mostly dhenry for the hint!
I am still open to new suggestions!

Cheers

psyche:
The same happens if i use brackets and if a is cast to a long! This is really weird!

Yes, that is really wierd. I second AWOL's call for your code (using long) and the result of running that code. You seem to be constructing a 20-bit value from five four-bit values, and it should be possible to achieve that.

MarkT:
Its more subtle than that - by default an expression is promoted to the widest type present in it.
Here the constant 16 is of type int and is thus 16 bits on the Arduino. An expression of the form

Mostly true, though not complete.address = ((long)a)<<16 | b<<12 | c<<8 | d<<4 | e;

  • If you have a mixture of just char and short items, the expression is converted to int
  • The right side of a shift does not cause the left side to be promoted
  • The promotion does not occur until the sub-expression is validated, so in your example, (b<<12) would still be evaluated as an int. If b happened to be 16..31, it will create a negative number, which when converted to long will fill in all of the upper bits.

psyche:
I tried that too but it didn't work! I also updated to 1.0.1 but still no luck.

Can you please post what you tried, and what you got. Saying I "tried that" and it "didn't work" is very non-specific.

My code here:

void setup ()
{
  Serial.begin (115200);
}

void loop ()
{
  long address;
  
  byte a = 0x0F;
  byte b = 0x0E;
  byte c = 0x0C;
  byte d = 0x0D;
  byte e = 0x0A;
  
  address = long (a) << 16 | long (b) << 12 | c << 8 | d << 4 | e;
  
  Serial.println (address, HEX);
  
  while (1);
}

Works. Output:

FECDA

So I can't really believe you tried it and it didn't work.

Ok the long (b) << 12 did the job BUT it doesn't work in the for loop!
Any reason why? If i remove the 2 "long" then the address is increased as it should! With the 2 "long" in there, what i get is the initial value and then i get the last value! Not any other values between the two, just min and max values! Weird!!!

Show us the whole code and output as it is now - there is clearly another issue now. In particular str1, str2, str3 etc - what types/values are they.

Using union or pointers to reconstitute a different data type is actually fairly common.

Yes, when the constituent types align to the platform's native word size.

Still waiting to see dhenry's code to perform the equivalent of a four bit shift with pointers and/or structs/unions.

AWOL:
Still waiting to see dhenry's code to perform the equivalent of a four bit shift with pointers and/or structs/unions.

I have a suspicion that it might be possible to achieve using bitfields, but even assuming you could muddle a way through to make it work the result would be pretty convoluted. Straight forward bit shifts offer a far more appropriate solution in this case.