Link to home
Start Free TrialLog in
Avatar of viki2000
viki2000Flag for Germany

asked on

C Language combined operators

I have next expressions:
1)      DataValue += (1<<i);
2)      if( ((DataValue>>i)&0x1) == 0x1 )
         {
            ; //Do something
         }
I need help to understand step by step what happens in the expressions above.
Could you write them in expanded format, simple line by line, eventually with some explanations?
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

There are a few bit operations going on there.  I would advise you to read about them eg. the following in wikipedia:
https://en.wikipedia.org/wiki/Bitwise_operations_in_C

Then try to expand them yourself.  (It is so easy to read something and say 'great, I understand it' and then a couple of days later you realise you still don't have a clue.  Perfroming the expansion yourself helps fix it in your mind what is going on.

Post what your expansion is and I can say if you have done it correctly.
Avatar of viki2000

ASKER

Wrong answer :)
User generated image
My trouble was with "+=" and with shift combined with logical operators.
Your link does not provide description for that.
In exchange are a tones of other links with examples:
https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B
http://www.tutorialspoint.com/cprogramming/c_operators.htm
http://www.cs.mun.ca/~michael/c/op.html
http://www.cplusplus.com/doc/tutorial/operators/
http://www.cs.utexas.edu/users/fussell/cs310h/lectures/Lecture_16-310h.pdf

Not a tutorial is what I need, especially that I am not a daily programmer.
But I will try to sweat one more time :(
Let’s take the 1) example, because it is easier : ) )

y += x;       //is in fact:
y = y + x;      

DataValue += (1<<i);
DataValue = DataValue + (1<<i);

<<  //Shift bits left
So 1 in decimal is 00000001 in binary and that last 1 from right side will be shifted towards left with “i” positions.
Now comes the question: when is shifted, before or after is added to DataValue? I assume before, correct me if I am wrong.

Let’s introduce a new variable “temp”, assuming is type char 8bits as all the other above.
So, the expression becomes:
temp = 1<<i;
DataValue = DataValue + temp

Something like that?...at least I tried…
SOLUTION
Avatar of AndyAinscow
AndyAinscow
Flag of Switzerland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
To your previous comment.  All correct.  

putting something in () makes that be computed first, otherwise there is something called operator precedence.  (I like using brackets - it is clear what one intends).
ps.  0x1 means use the hexadecimal notation.  0x1 is exactly the same as 1.  0x9 is 9, 0xA is 10 up to 0xF which is 15 then 0x10 is sixteen....
Lets’ try 2):
if( ((DataValue>>i)&0x1) == 0x1 )
         {
            ; //Do something
         }
Assume we use again a variable „temp”, then:

temp = DataValue >> i; //we shift right first the bits in DataValue with i positions
temp = temp AND 0x1; // here seems that we apply a mask with 0x1 and only the last bit remains, th rest is made 0
if (temp == 0x1) // here we test the last bit from DataValue to see if it is 1
         {
            ; //Do something
         }
If I understand right, the decision to do something is taken when the last bit in DataValue, after shift right, is 1.

Is it OK?
Again correct.

>>here seems that we apply a mask with 0x1 and only the last bit remains, th rest is made 0
Actually nothing is made zero, the result is the comparison of those two bits.
"temp = temp AND 0x1; // here seems that we apply a mask with 0x1 and only the last bit remains, the rest is made 0"
Actually nothing is made zero, the result is the comparison of those two bits.

0x1 in HEX is 0b00000001 in binary, assuming we work with 8bit variables.
Then, when we apply “&”, which is “AND” at binary level, then knowing “0” AND “anything” = 0 we get “0” for any other bit position from 1st left (MSB) to 7th position, near the last bit on the right side (LSB). In other words all 7 bits are made 0 in the variable temp. That’s why I said “the rest is made 0”.

Example:
temp = 0x7; // that is 0b00000111
temp = temp & 0x1;
// that means temp = 0b00000111 & 0b00000001
// then temp  becomes temp = 0b00000001 , so temp = 0x1

Is it not like that?

If it is like that, then temp becomes  equal with 0x1 in all the cases except when temp has initial value which has the last bit 0, then it becomes equal with 0x0, all the bits 0.
Good to remember that we have to make the difference between “&” which is at binary level making operation bit with bit, and then is “&&” which applies to the entire variable/number.
Actually I have to translate the initial C logic with combined operators into a flowchart logical diagram. I tried it below.
For the second expression I tried to eliminate the variable temp.
Please double check it and tell me if its right. Then we are done.
User generated imageUser generated image
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
You say that 1st diagram is correct, but the 2nd not and needs a correction.
In your code above:
"
Second you have:
DataValue = DataValue >>1
DataValue = DataValue & 0x1
DataValue==0x1  YES - DoSomething,  No - continue
"


Actually is "i" not "1":
DataValue = DataValue >>i
DataValue = DataValue & 0x1
DataValue==0x1  YES - DoSomething,  No - continue

But speaking about logic and using temporary variables, do you think the above code above is not working ? And if I compare it with yours above written in C as screenshot, do you think I get different results? I just wanted to eliminate variable "temp" and use DataValue as much as possible, that will save one memory location.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Few more comments to AndyAinscow:
Agree that if I do not use t1 and/or t2 as temporary variable, but rather to use DataValue as variable, then its value is changed. But if I do not care about that and I care only about the decision in “if” for “k”, then I suppose everything is fine. And such case may apply when we work with very low RAM memory and each variable will count. I will provide an example later.
As I did not have in my PC any C/C++ compiler for x86 or x64, so everything was theoretical for me up to now, I deceided to install a IDE for C and a compiler.
After I spent some time with Sara on another question how to setup some free compilers, I decided for Netbeans and Cygwin.
I wanted to reproduce your code, my flowchart and later what Sara proposed – just to analyze it and to have it fixed better in my mind.
Your code in Netbeans with Cygwin:
User generated imageThen my 2nd flowchart implemented in C – we see k = 0, but DataValue is change to 0 instead of 37, so is modified :
User generated imageThen what you proposed as equivalent to my 2nd flowchart using t1 and t2 temporary variables, so DataValue is not changed and k=0, meaning the same decision:
User generated imageThen another flowchart, where DtaValue is not changed, but I use only one “temp” variable instead of t1 and t2:
User generated imageWith the C code here:
User generated imageI will come later to Sara’s comments.
Coming at Sara’s comments.
Your assumption “the += operation probably is wrong and most likely should be replaced by |= ” is not OK.
I think it should be +=
The 2nd observation “if ((datavalue & (1<<i)) != 0)” is definitely good, but not the 1st one.
I tested your code, with “+=” and with “|=”. See the results below. They are different and far from what everything else we tested up to now.
I will bring more explanations later about where is such code applied, for what and what processors and compilers involved.
User generated imageUser generated image
These operations are part from a code written in C for microcontrollers.
The intention is to implement serial RS232 functionality to the microcontrollers which does not have UART/USART hardware and the communication protocol will be done software. From hardware point of view will be only 2 digital puns used.
The code I found on internet here:
http://saeedsolutions.blogspot.de/2012/07/pic12f675-software-uart-bit-banging.html 
The author used MPLAB v8.85 IDE with HI-TECH C v9.83 compiler, but few years ago I used also MPLABX with XC8 C compiler. I implemented only the TX function, bit banging without any protection against error, just plain serial and was OK.
The intention was to implement on small PIC microcontroller which have 32 or max. 64 RAM, with 256 or 512 flash memory, besides other small functions.
Of course I could have provided screenshots from MPLABX with XC8 C compiler working with simulator, but I was afraid of misunderstanding.
To complicate (or perhaps simplify) the situation, the code must be also ported to another IDE, a graphical language for microcontrollers with its own C compiler, which accepts also ASM if it is needed. I speak about Flowcode (https://www.matrixtsl.com/flowcode/ ) – from here my intention to show also flowcharts as equivalent to the code above. Luckily Flowcode accepts combined operators, so I do not have to change anything if I want a compact flow diagram. The expansion of the operations was only for my understanding.
I have also some examples of similar code written directly in ASM, but that I did not use since more than 20 years. Being small, I think I get through it and I might use it only if the C code is too big for a small PIC with only few bytes of RAM and ROM.
Of course the C code is not so complicated and in the past, maybe 20 years ago I made some C /C++ projects, building DAQ board for PC, written in C, not visual C, around 50 screens. Since then only occasionally I play with some microcontrollers, but I am not a daily programmer. I can do it if is needed, when is needed with some help to remember even the syntax for different IDE or compilers. I think I made more VBA than C in the last 10 years, again occasionally.
Well, believe it or not, even if I have the base for programming and I did some, also industrial PLC’s, was never needed to use combined operators – from here my questions.
If I have time, next days I will go into deep and I will analyses the proposed code found on internet for RS232 bit bang and I can answer to Sara if she is indeed right or wrong, but as I remember 2-3 years ago when I tested the code for TX was OK with “+=” and not with “|=”.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi Sara, let’s go into the deep.
Attached is the proposed code. Below are the RX and TX subroutines.
For RX is used the 1) code from my question and for TX is used 2) code.
We work with 8bit variables, so DataBitCount is 8.
void UART_Transmit(const char DataValue)
{
   /* Basic Logic
      
      TX pin is usually high. A high to low bit is the starting bit and 
      a low to high bit is the ending bit. No parity bit. No flow control.
      BitCount is the number of bits to transmit. Data is transmitted LSB first.

   */

   // Send Start Bit
   UART_TX = 0;
   __delay_us(OneBitDelay);

   for ( unsigned char i = 0; i < DataBitCount; i++ )
   {
      //Set Data pin according to the DataValue
      if( ((DataValue>>i)&0x1) == 0x1 )   //if Bit is high
      {
         UART_TX = 1;
      }
      else      //if Bit is low
      {
         UART_TX = 0;
      }

       __delay_us(OneBitDelay);
   }

   //Send Stop Bit
   UART_TX = 1;
   __delay_us(OneBitDelay);
}

Open in new window

unsigned char UART_Receive(void)
{
   // Pin Configurations
    // GP1 is UART RX Pin

   unsigned char DataValue = 0;

   //wait for start bit
   while(UART_RX==1);

   __delay_us(OneBitDelay);
   __delay_us(OneBitDelay/2);   // Take sample value in the mid of bit duration

   for ( unsigned char i = 0; i < DataBitCount; i++ )
   {
      if ( UART_RX == 1 )   //if received bit is high
      {
         DataValue += (1<<i);
      }

      __delay_us(OneBitDelay);
   }

   // Check for stop bit
   if ( UART_RX == 1 )       //Stop bit should be high
   {
      __delay_us(OneBitDelay/2);
      return DataValue;
   }
   else                      //some error occurred !
   {
      __delay_us(OneBitDelay/2);
      return 0x000;
   }
}

Open in new window

We focus now first on TX.
The idea is to take bit by bit all 8bits from a 8bit variable and send each bit at TX pin one by one.
We use a for loop with index i from 0 to 7.
We start with one bit “0” logic for Start of transmission.
Each bit from DataValue variable is shifted right, one at the time, in the for loop, until lands on the LSB position, the most right position. The DataValue is not changed, so a temporary register is used as Andy mentioned. Then a mask at bit level is used (0x1=0b00000001) which makes/forces all the bits to be “0” in the temporary unseen register, except the last one which can be 0 or 1 depending by its value, which remains unchanged. So it is a filter to prepare the data to be compared with second (0x1=0b00000001), which in fact means test the LSB from DataValue, shifted with i positions, to see if it is 1 or 0. Then the decision is taken to make high or low the hardware pin. The rest is just delay to maintain the bit 1 or 0 so long time as is needed to match the chosen baud rate. In the end we send one bit “1” logic for Stop of the transmission.
I think the logic is quite clear: just take each bit from DataValue and test if it is 0 or 1 and then we activate hardware a pin to be  o or 1 in the same way. To do that we use shift right (>>i) each bit, then mask (&0x1) and test (==0x1).
I can only say here that Andy was right when pointed to the fact that DataValue must remain unchanged, so explanation with additional temporary variables t1 and t2 are good, or using one temp variable, probably internal registers of the CPU, not directly seen in the C code, but probably seen in ASM code.

Using your proposed code Sara “if ((datavalue & (1<<i)) != 0)” just move the shifting from DataValue to i and the mask changes all the time, instead of being all 7 bits from left to right 0, now the mask changes according to each bit position. Then you decided to use instated “==0x1” the “!=0”.
I cannot say it now for sure, because I already understood the logic in the first proposed code,  but I believe your proposed code is more easy readable/understandable at the first look.

Let’s focus now on RX.
Normally the line is “1” all the time waiting, as is set by Stop bit at transmission.
When a byte starts then it has the first bit “0” logic. So we wait in while loop until we have that first bit of “0” logic and after that “0” start bit we delay on more half of the bit to be in the middle of the rectangular pulse/signal/bit just to make sure we read right/proper the state of the bit: 0 or 1.
Then we use again a for loop with index i from 0 to 7, so 8 bits, and we test at an interval of time equal with “OneBitDelay” given by the chosen baud rate, we test if the hardware pin for RX is “1” logic.
In case RX pin is “1” logic then we set the corresponding bit position in DataValue, according with “i” value.
As DataValue was made equal with 0 in the beginning, so all bits were 0, then only the tested bits found equal with “1” were made “1” logic.
In the end we check for Stop bit and we return the DataValue as RX result with a nice RX data as set in for loop with if decision or we return the value of 0x0 in case of error or.
Let’s look at the heart of the RX, the “DataValue += (1<<i);”.

We can write it indeed DataValue = DataValue  + 2^i
Let’s continue the example above with DataValue = 37 and i=2 and we look at the bit level.
In other words RX pin was “1” logic when i=2, meaning from 0 to 2, we are now on the 3rd bit position of DataValue.
DataValue=37, we suppose in decimal, so DataValue=0x25 in hexadecimal and binary DataValue=0b00100101
Then if i=2 in decimal we have (1<<i) =(1<<2)=2^2=4 in decimal and 0b00000100 in binary.
The operation “DataValue += (1<<i);” becomes DataValue = 37 + 4 = 41 in decimal, which is 0b00101001 in binary.
If we compare now at bit level 37(0b00100101) with 41(0b00101001) we see, as you already mentioned, bit#3 is reset (made 0) and bit#4 is set (made 1), because we add power of 2.

If we do  “DataValue |= (1<<i);”, which means “DataValue = DataValue  | (1<<i);” we would have DataValue = 37|4
That is DataValue = 0b00100101 OR 0b00000100 = 0b00100101 = 37, because the bit#3 was already “1” in 37.
It is clear not the same result.
We need to set bits in DataValue when we have RX pin “1”.

Maybe for any other code where DataValue can have any value, then the “+=” operation should be maintained if it has a defined and clear explained purpose.
But here, I think Sara is right and the “|=” maybe be replaced. I will explain below why.

The chosen example, chosen random by Andy, is not proper for RX-TX transmission, of course he did not know that, because we discussed only theoretically with numbers and operators.

When i=2 the DataValue can never have the value 37. When i=2, before we apply the operation DataValue can be max 0b00000011, so 3 index decimal, because was 0b00000000 in the beginning and we moved only 2 positions with index I as i=0 and i=1, then we arrived to i=2. That is important because we cannot have the bit#3 set before operation when i=2, never. It is 0, always. It can change only after we apply “+=” or “|=” operations.

Let’s take a proper example for RX.
Let’s suppose the data that comes at RX pin looks like 0b01011001 for example, random chosen. That is 89 in decimal.
We try first with “+=”. Instead of DataValue I will use DV to be shorter.
We read from right to left, LSB to MSB as direction for RX.
Initial DV=0=0x0=0b00000000
We use DV=DV + 2^i

i=0 then DV= 0 + 2^0 = 1 = 0b00000001
i=1 then DV = 1 = 0b00000001
i=2 then DV = 1 = 0b00000001
i=3 then DV = 1 + 2^3 = 9 = 0b00001001
i=4 then DV = 9 + 2^4 = 25 = 0b00011001
i=5 then DV = 25 = 0b00011001
i=6 then DV = 25 + 2^6 = 89 =0b01011001
i=7 then DV = 89 = 0b01011001

Let’s try now with “|=”. Then we use DV=DV | 2^i

i=0 then DV= 0 | 2^0 = 0b00000000 | 0b00000001 = 0b00000001
i=1 then DV = 0b00000001 = 1
i=2 then DV = 0b00000001 = 1
i=3 then DV = 1 | 2^3 = 0b00000001 | 0b00001000 = 0b00001001 = 9
i=4 then DV = 9 | 2^4 = 0b00001001 | 0b00010000 = 0b00011001 = 25
i=5 then DV = 25 = 0b00011001
i=6 then DV = 25 | 2^6 = 0b00011001 | 0b01000000 = 0b01011001 = 89
i=7 then DV = 89 = 0b01011001

We get the same results.
The only confusion, at least for me, arrived from wrong chosen 37 for DataValue when i=2, which is not possible such transmission when DataValue start with 0.
As conclusion, Sara was right again with her observation of using ”|=” to set a bit, but here particularly both combined operators may be used "+=" as well as  ”|=”.
Thank you Sara and thank you Andy for the assistance.
UART_FULL.TXT
User generated image
i=1 then DV = 1 = 0b00000001
i=2 then DV = 1 = 0b00000001

Open in new window


shouldn't it be the following?

i=1 then DV = 1 + 2^1 = 3 = 0b00000011
i=2 then DV = 3 + 2^2 = 7 = 0b00000111

Open in new window


simply said, if you add x = 2^i to an integer which is less than x you effectively set bit i to 1 same as with or.

note, using a char type as a 8-bit integer could be dangerous since for most compilers char is a signed integer. so you effectively have only 7 bits and 1 sign bit.  that doesn't matter much if you play with bits, but for operations like plus or minus or less or greater this could be fatal. so i would recommend to using unsigned char (BYTE) which makes less trouble. another issue could arise when using terms like (1<<i) since these terms are 32-bit integers. if one operand is 8-bit integer and the another operand is an 32-bit integer the results might get odd or even undefined. using a c++ Compiler you would get warnings or even errors if you do so. in ansi c you could use temporary variables to avoid such issues.


Sara
No, it is not:

i=1 then DV = 1 + 2^1 = 3 = 0b00000011
i=2 then DV = 3 + 2^2 = 7 = 0b00000111

Open in new window


It is:
i=1 then DV = 1 = 0b00000001
i=2 then DV = 1 = 0b00000001

Open in new window


Because for i=1 and i=2 the bits from right side of the number 89 seen in binary 0b01011001  are "0" so no decision if is taken sand no operation DV=DV+2^i is done. It is just skipped. The operation is done only when the RX bit at the hardware pin is "1".

another issue could arise when using terms like (1<<i) since these terms are 32-bit integers

No. These tiny microcontrollers do not work with 32bit integers. They work only with 8bit. Their C compiler knows that.
This is not for x86 machine. It is for something like that:
http://www.microchip.com/wwwproducts/en/PIC10F200
http://ww1.microchip.com/downloads/en/DeviceDoc/40001239F.pdf
Glad you have it sorted out now.  I hope you understand why I wanted you to think it through yourself rather than just writing an explanation.  

I'd not abandoned this question, just I had an unexpected visit to the hospital lasting many days.
No. These tiny microcontrollers do not work with 32bit integers.

you may try

int x = sizeof((1<<i));

to find out the size (number of bytes) of temporary integers. i would assume it is at least 2 (16 bits).

Sara
The C compilers used are Hi-Tech C complier for PIC10/12/16 with the user manual here:
http://ww1.microchip.com/downloads/en/DeviceDoc/HTC_PIC_manual.pdf
or, after was bought by Microchip and not developed anymore, but in exchange on top of it they made their own complier named XC8:
http://ww1.microchip.com/downloads/en/DeviceDoc/50002053F.pdf
Both support sizeof function and i can try it, but in in fact is not really needed.
The only real competition comes from CCS C compiler:
https://www.ccsinfo.com/downloads/ccs_c_manual.pdf
If inside the code we define a 32 bit integer, then it will take 4 memory locations because it is a 8bit processor.
On the page 6 of the datasheet:
http://ww1.microchip.com/downloads/en/DeviceDoc/40001239F.pdf
we read:
"
The  ALU  is  8  bits  wide  and  capable  of  addition,
subtraction,   shift   and   logical   operations.   Unless
otherwise  mentioned,  arithmetic  operations  are  two’s
complement in nature. In two-operand instructions, one
operand is typically the W (working) register. The other
operand  is  either  a  file  register  or  an  immediate
constant. In single operand instructions, the operand is
either the W register or a file register.
The W register is an 8-bit working register used for ALU
operations. It is not an addressable register.
"


So, everything is on 8bit, the Arithmetical and Logical Unit and the Working Register. In the "int x = sizeof((1<<i));" is 99% expected to have x=8.
that looks fine. so you are safe with 8 bit operations.

did you find out whether char is signed or unsigned?

Sara
If you look at ID: 41567541 attached is a file with UART_FULL C code.
We can see that is unsigned char.