I just come across the statement in embedded c (dsPIC33)
sample1 = sample2 = 0;
Would this mean
sample1 = 0;
sample2 = 0;
Why do they type it this way? Is this good or bad coding?
Remember that assignment is done right to left, and that they are normal expressions. So from the compilers perspective the line
sample1 = sample2 = 0;
is the same as
sample1 = (sample2 = 0);
which is the same as
sample2 = 0;
sample1 = sample2;
That is, sample2 is assigned zero, then sample1 is assigned the value of sample2. In practice the same as assigning both to zero as you guessed.
Formally, for two variables t and u of type T and U respectively
T t;
U u;
the assignment
t = u = X;
(where X is some value) is interpreted as
t = (u = X);
and is equivalent to a pair of independent assignments
u = X;
t = (U) X;
Note that the value of X is supposed to reach variable t "as if" it has passed through variable u first, but there's no requirement for it to literally happen that way. X simply has to get converted to type of u before being assigned to t. The value does not have to be assigned to u first and then copied from u to t. The above two assignments are actually not sequenced and can happen in any order, meaning that
t = (U) X;
u = X;
is also a valid execution schedule for this expression. (Note that this sequencing freedom is specific to C language, in which the result of an assignment in an rvalue. In C++ assignment evaluates to an lvalue, which requires "chained" assignments to be sequenced.)
There's no way to say whether it is a good or bad programming practice without seeing more context. In cases when the two variables are tightly related (like x and y coordinate of a point), setting them to some common value using "chained" assignment is actually perfectly good practice (I'd even say "recommended practice"). But when the variables are completely unrelated, then mixing them in a single "chained" assignment is definitely not a good idea. Especially if these variables have different types, which can lead to unintended consequences.
I think there is no good answer on C language without actual assembly listing :)
So for a simplistic program:
int main() {
int a, b, c, d;
a = b = c = d = 0;
return a;
}
I've got this assemly (Kubuntu, gcc 4.8.2, x86_64) with -O0 option of course ;)
main:
pushq %rbp
movq %rsp, %rbp
movl $0, -16(%rbp) ; d = 0
movl -16(%rbp), %eax ;
movl %eax, -12(%rbp) ; c = d
movl -12(%rbp), %eax ;
movl %eax, -8(%rbp) ; b = c
movl -8(%rbp), %eax ;
movl %eax, -4(%rbp) ; a = b
movl -4(%rbp), %eax ;
popq %rbp
ret ; return %eax, ie. a
So gcc is actually chaining all the stuff.
sample1 = sample2 = 0;
does mean
sample1 = 0;
sample2 = 0;
if and only if sample2 is declared earlier.
You can't do this way:
int sample1 = sample2 = 0; //sample1 must be declared before assigning 0 to it
You can yourself decide that this way of coding is good or bad.
Simply see the assembly code for the following lines in your IDE.
Then change the code to two separate assignments, and see the differences.
In addition to this, you can also try turning off/on optimizations (both Size & Speed Optimizations) in your compiler to see how that affects the assembly code.
The results are the same. Some people prefer chaining assignments if they are all to the same value. There is nothing wrong with this approach. Personally, I find this preferable if the variables have closely related meanings.
As noticed earlier,
sample1 = sample2 = 0;
is equal to
sample2 = 0;
sample1 = sample2;
The problem is that riscy asked about embedded c, which is often used to drive registers directly. Many of microcontroller's registers have a different purpose on read and write operations. So, in gereral case, it is not the same, as
sample2 = 0;
sample1 = 0;
For example, let UDR be a UART data register. Reading from UDR means getting the recieved value from the input buffer, while writing to UDR means putting the desired value into transmit buffer and hitting the communication. In that case,
sample = UDR = 0;
means the following: a) transmit value of zero using UART (UDR = 0;) and b) read input buffer and place data into sample value (sample = UDR;).
You could see, the behavior of embedded system could be much more complicated than the code writer may expect to be. Use this notation carefully while programming MCUs.
Regarding coding style and various coding recommendations see here:
Readability a=b=c or a=c; b=c;?
I beleive that by using
sample1 = sample2 = 0;
some compilers will produce an assembly slightly faster in comparison to 2 assignments:
sample1 = 0;
sample2 = 0;
specially if you are initializing to a non-zero value. Because, the multiple assignment translates to:
sample2 = 0;
sample1 = sample2;
So instead of 2 initializations you do only one and one copy. The speed up (if any) will be tiny but in embedded case every tiny bit counts!
As others have said, the order in which this gets executed is deterministic. The operator precedence of the = operator guarantees that this is executed right-to-left. In other words, it guarantees that sample2 is given a value before sample1.
However, multiple assignments on one row is bad practice and banned by many coding standards (*). First of all, it is not particularly readable (or you wouldn't be asking this question). Second, it is dangerous. If we have for example
sample1 = func() + (sample2 = func());
then operator precedence guarantees the same order of execution as before (+ has higher precedence than =, therefore the parenthesis). sample2 will get assigned a value before sample1. But unlike operator precedence, the order of evaluation of operators is not deterministic, it is unspecified behavior. We can't know that the right-most function call is evaluated before the left-most one.
The compiler is free to translate the above to machine code like this:
int tmp1 = func();
int tmp2 = func();
sample2 = tmp2;
sample1 = tmp1 + tmp2;
If the code depends on func() getting executed in a particular order, then we have created a nasty bug. It may work fine in one place of the program, but break in another part of the same program, even though the code is identical. Because the compiler is free to evaluate sub-expressions in any order it likes.
(*) MISRA-C:2004 12.2, MISRA-C:2012 13.4, CERT-C EXP10-C.
Take care of this special case ...
suppose b is an array of a structure of the form
{
int foo;
}
and let i be an offset in b. Consider the function realloc_b() returning an int and performing the reallocation of array b. Consider this multiple assignment:
a = (b + i)->foo = realloc_b();
To my experience (b + i) is solved first, let us say it is b_i in the RAM ; then realloc_b() is executed. As realloc_b() changes b in RAM, it results that b_i is no more allocated.
Variable a is well assigned but (b + i)->foo is not because b as been changed
bythe execution of the most left term of the assignment i.e. realloc_b()
This may cause a segmentation fault since b_i is potentially in an unallocated RAM location.
To be bug free, and to have (b + i)->foo equal to a, the one-line assignment must be splitted in two assignments:
a = reallocB();
(b + i)->foo = a;
Related
What is the real advantage of using compound assignment in C/C++ (or may be applicable to many other programming languages as well)?
#include <stdio.h>
int main()
{
int exp1=20;
int b=10;
// exp1=exp1+b;
exp1+=b;
return 0;
};
I looked at few links like microsoft site, SO post1, SO Post2 .
But the advantage says exp1 is evaluated only once in case of compound statement. How exp1 is really evaluated twice in first case? I understand that current value of exp1 is read first and then new value is added. Updated value is written back to the same location. How this really happens at lower level in case of compound statement? I tried to compare assembly code of two cases, but I did not see any difference between them.
For simple expressions involving ordinary variables, the difference between
a = a + b;
and
a += b;
is syntactical only. The two expressions will behave exactly the same, and might well generate identical assembly code. (You're right; in this case it doesn't even make much sense to ask whether a is evaluated once or twice.)
Where it gets interesting is when the left-hand side of the assignment is an expression involving side effects. So if you have something like
*p++ = *p++ + 1;
versus
*p++ += 1;
it makes much more of a difference! The former tries to increment p twice (and is therefore undefined). But the latter evaluates p++ precisely once, and is well-defined.
As others have mentioned, there are also advantages of notational convenience and readability. If you have
variable1->field2[variable1->field3] = variable1->field2[variable2->field3] + 2;
it can be hard to spot the bug. But if you use
variable1->field2[variable1->field3] += 2;
it's impossible to even have that bug, and a later reader doesn't have to scrutinize the terms to rule out the possibility.
A minor advantage is that it can save you a pair of parentheses (or from a bug if you leave those parentheses out). Consider:
x *= i + 1; /* straightforward */
x = x * (i + 1); /* longwinded */
x = x * i + 1; /* buggy */
Finally (thanks to Jens Gustedt for reminding me of this), we have to go back and think a little more carefully about what we meant when we said "Where it gets interesting is when the left-hand side of the assignment is an expression involving side effects." Normally, we think of modifications as being side effects, and accesses as being "free". But for variables qualified as volatile (or, in C11, as _Atomic), an access counts as an interesting side effect, too. So if variable a has one of those qualifiers, a = a + b is not a "simple expression involving ordinary variables", and it may not be so identical to a += b, after all.
Evaluating the left side once can save you a lot if it's more than a simple variable name. For example:
int x[5] = { 1, 2, 3, 4, 5 };
x[some_long_running_function()] += 5;
In this case some_long_running_function() is only called once. This differs from:
x[some_long_running_function()] = x[some_long_running_function()] + 5;
Which calls the function twice.
This is what the standard 6.5.16.2 says:
A compound assignment of the form E1 op= E2 is equivalent to the simple assignment expression E1 = E1 op (E2), except that the lvalue E1 is evaluated only once
So the "evaluated once" is the difference. This mostly matters in embedded systems where you have volatile qualifiers and don't want to read a hardware register several times, as that could cause unwanted side-effects.
That's not really possible to reproduce here on SO, so instead here's an artificial example to demonstrate why multiple evaluations could lead to different program behavior:
#include <string.h>
#include <stdio.h>
typedef enum { SIMPLE, COMPOUND } assignment_t;
int index;
int get_index (void)
{
return index++;
}
void assignment (int arr[3], assignment_t type)
{
if(type == COMPOUND)
{
arr[get_index()] += 1;
}
else
{
arr[get_index()] = arr[get_index()] + 1;
}
}
int main (void)
{
int arr[3];
for(int i=0; i<3; i++) // init to 0 1 2
{
arr[i] = i;
}
index = 0;
assignment(arr, COMPOUND);
printf("%d %d %d\n", arr[0], arr[1], arr[2]); // 1 1 2
for(int i=0; i<3; i++) // init to 0 1 2
{
arr[i] = i;
}
index = 0;
assignment(arr, SIMPLE);
printf("%d %d %d\n", arr[0], arr[1], arr[2]); // 2 1 2 or 0 1 2
}
The simple assignment version did not only give a different result, it also introduced unspecified behavior in the code, so that two different results are possible depending on the compiler.
Not sure what you're after. Compound assignment is shorter, and therefore simpler (less complex) than using regular operations.
Consider this:
player->geometry.origin.position.x += dt * player->speed;
versus:
player->geometry.origin.position.x = player->geometry.origin.position.x + dt * player->speed;
Which one is easier to read and understand, and verify?
This, to me, is a very very real advantage, and is just as true regardless of semantic details like how many times something is evaluated.
Advantage of using compound assignment
There is a disadvantage too.
Consider the effect of types.
long long exp1 = 20;
int b=INT_MAX;
// All additions use `long long` math
exp1 = exp1 + 10 + b;
10 + b addition below will use int math and overflow (undefined behavior)
exp1 += 10 + b; // UB
// That is like the below,
exp1 = (10 + b) + exp1;
A language like C is always going to be an abstraction of the underlying machine opcodes. In the case of addition, the compiler would first move the left operand into the accumulator, and add the right operand to it. Something like this (pseudo-assembler code):
move 1,a
add 2,a
This is what 1+2 would compile to in assembler. Obviously, this is perhaps over-simplified, but you get the idea.
Also, compiler tend to optimise your code, so exp1=exp1+b would very likely compile to the same opcodes as exp1+=b.
And, as #unwind remarked, the compound statement is a lot more readable.
What is the difference in += and normal add
a = a + b;
a += b;
what is the different in above two lines?
Is there any increase in CPU cycles for "+=" operator?
Which would be the preferable method of coding.?
Beside single evaluation of first operand, there is second difference, that occurs when b is an expression, involving operators with lower precedence. For instance:
int a = 1;
a += 0 || 1;
yields two, while:
int a = 1;
a = a + 0 || 1;
stores one into a. The equivalent of former statement would be:
a = a + (0 || 1);
There is a difference in between them and it is explained in the C standard:
C11: 6.5.16.2 Compound assignment (p3):
Acompound assignment of the form E1 op= E2 is equivalent to the simple assignment expression E1 = E1 op (E2), except that the lvalue E1 is evaluated only once, and with respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation.
When I compile the following two programs,
int main(int argc, char** argv)
{
int a = 5, b = 10;
a = a + b;
return 0;
}
and
int main(int argc, char** argv)
{
int a = 5, b = 10;
a += b;
return 0;
}
with gcc file.c -S -O0, I get the following assembler output (this is just the relevant section)
movl $5, -4(%rbp) # Assign 5 to a
movl $10, -8(%rbp) # Assign 10 to b
movl -8(%rbp), %eax # Move b to eax
addl %eax, -4(%rbp) # Add eax to a, store in a
This same output is produced with both implementations.
However, although everything works out nicely with integers and a single addition, there are cases where you may get different results, consider for double a,b the operation a *= b*b and a = b*a*b. If the result of the particular multiplication can't be exactly represented then the two operations will produce different results.
Both the expressions are same. Second one is short hand assignment operator. Which will help us to reduce the reptitive coding.
a = a + b; // Here a will come twice.
a += b;
Here the repitition of code will be avoided.
Difference is only in source. The output binary (executable) is exactly the same (with normal current compilers).
You might like one or the other better in different conditions.
I like the short form better, especially when a is complicated
vector[savedpos] = vector[savedpos] + 1000;
vector[savedpos] += 1000;
Here is a good explanation..Quoting from the source
x += y means
Find the place identified by x
Add y to it
But x = x + y means:
Evaluate x+y
Find the place identified by x
Copy x into an accumulator
Add y to the accumulator
Store the result in x
Find the place identified by x
Copy the accumulator to it
This question already has answers here:
Why don't people use xor swaps? [closed]
(4 answers)
Closed 7 years ago.
I have come across a "technique" for swapping 2 variables (ints, chars or pointers) without a third temp variable , like this :
int a = Something ;
int b = SomethingElse ;
a ^= b ;
b ^= a ;
a ^= b ;
Normal way would be :
int a = Something ;
int b = SomethingElse ;
int temp = a ;
a = b ;
b = temp ;
All this is fine, but the folks who share this "technique" usually state it as without using extra space.
(A) Is this true that there is no extra space ? I think, "memory to memory copy" would require fewer instructions (machine code) compared to "memory to memory XOR operation".
int temp = a <==> move content of memory location a to memory location temp **(1 instruction)**, possibly via a register **(2 instructions)**
a ^= b <==> move contents of memory location a to register1, move contents of memory location b to register2, xor register1 and register2 (store results in register1) , move register1 to memory location a **(about 4 instructions)**
It seems that the "technique" will result in longer code and longer runtime.
(B) Is the "technique" faster (or better) in some way or in some cases ?
It seems like the "technique" is slower, uses more memory, and not really suitable for floating points.
EDIT:
It seems that there may be some Potential Duplicates :
Why don't people use xor swaps?
But this question is obviously Different:
(A) That question was closed as "Not Constructive" , where it "will likely solicit debate, arguments, polling, or extended discussion", whereas this question is looking for factual references, eg "Is something true ?" & "Is this better ?"
(B) That question is about why people do not use the "technique", while this question is about the analysis of the "technique", without looking onto why people use it or do not use it.
There's no definitive answer: it depends too much on:
the architecture of the machine (number of processors, memory cache size, etc); and
the quality of the compiler's optimisation.
If all the operations are performed within registers, there is unlikely to be a performance penalty of XOR compared with copy.
If you use a third variable you could help the compiler by declaring:
register int temp;
The use of the XOR technique (or addition and subtraction, as in a-=b; b+=a; a=b-a;) dates back to when memory was a crucial resource and saving a stack entry could be very important. These days the only value of this is code obfuscation.
I have absolutely no idea what the effect of XOR would be on floating-point values, but I suspect they might be converted to integers first: you will need to try it, but there is no guarantee that all compilers will give the same result.
For lower level (e.g. assembly) "variables" no longer have a permanent location. Sometimes a variable will be in memory, but sometimes the same variable will be in one register, and sometimes in a different register.
When generating code, the compiler has to keep track of where each variable happens to be at each point. For an operation like a = b, if b is in register1 then the compiler just decides that a is now also in register1. Nothing needs to be moved or copied.
Now consider this:
// r0 contains variable a
// r1 contains variable b
temp = a
// r0 contains variable a and variable temp
// r1 contains variable b
a = b
// r0 contains variable temp
// r1 contains variable b and variable a
b = temp
// r0 contains variable temp and variable b
// r1 contains variable a
If you think about this you'll realise that no data needs to be moved or copied, and no code needs to be generated. Modern CPUs are able to do "nothing" extremely quickly.
Note: If a variable is a local variable (on the stack) and isn't in a register, then the compiler should be able to do the same "renaming" to avoid moving anything. If the variable is a global variable then things get more complicated - most likely causing a load from memory and a store to memory for each global variable (e.g. 2 loads and 2 stores if both variables are global).
For XOR (and addition/subtraction) the compiler might be able to optimise it so that it becomes nothing; but I wouldn't count on it. The temporary variable moves are much more likely to be optimised well.
Of course not everything is small enough to fit in a register. For example, maybe you're swapping structures and they're 1234 bytes each. In this case the programmer might use memcpy() and the compiler has to do the copies; or the programmer might do one field at a time with XOR (or addition/subtraction) and the compiler has to do that. For these the memcpy() is much more likely to be better optimised. However, maybe the programmer is smarter, and doesn't swap the 1234 bytes of data and only swaps pointers to the data. Pointers fit in registers (even when the data they point to doesn't), so maybe no code is generated for that.
If you are manipulating ints or doubles (or more generally, any type that hold addition and subtraction operators), you can do it like this :
int a = 5;
int b = 7;
a += b; // a == 12, b == 7
b = a - b; // a == 12, b == 5
a -= b; // a == 7, b == 5
I would like to know what happens in memory when I use arithmetics operators like:
int i;
i = 5 + 3;
Will the values 5 and 3 be automatically put into the stack temporarily (like if some static variables were automatically created for them)? I suppose they need to exist somewhere for the addition to happen, so where?
What happens when there is a function call involved?
i = 5 + f(3);
Is the argument 3 passed to f stored somewhere? And what about the return value of f (say f returns an int)?
Many thanks,
Your first example will be evaluated at compile-time (see http://en.wikipedia.org/wiki/Constant_folding), so let's ignore that one.
In the case of something like i = f(3) + g(5), the compiler has many choices on how to implement this, depending on the particular platform you're working on. It may put things in registers, or on the stack, or even elsewhere, as it sees fit.
It's not required by the C spec that those values be put into memory. The implementation can keep them in registers and never store them on memory for carrying out the addition or the compiler can even rewrite 5 + 3 into 8 and don't do any addition at all at runtime. Any real implementation does it like that.
In the language theory, in fact, 5 and 3 aren't referring to memory. They are values (instead of merely representing a location where a value can be fetched from). You can easily see that by trying to write &5. It won't work - there is no address you could receive.
Have a look at the compiler switches. Possible there is the possibility to keep/generate the intermediary assembler code. There you see exaclty what happens with your code.
The compiler will allocate temporary variables as it needs them. This is done at compile/optimization time. Not at runtime (for C programs, anyway). Most likely, for your example, the result of the
5 + 3
or
5 + f(3)
will be stored in a register and the value of the register will be copied to the location of i.
When you have expression such as i = 5 + 3 there's no needing to put the operands on the stack. The compiler will translate that into something similar to:
mov eax, 5 // load first operand
add eax, 3 // compute the sum
mov [ebp - 4], eax // and store it
The operands are hard-coded in the instruction. Some compiler might decide to make this code like int i = 8.
i = 5 + f(3)
In this case 3 will be pushed on the stack because f has to be called, but its return value is (usually) stored in eax. This assembly code might be a good translation:
push 3 // f's argument
call f // call f, return value is in eax
add eax, 5 // compute the sum
mov [ebp - 4], eax // and save in i
I have a quick question, suppose I have the following code and it's repeated in a simliar way 10 times for example.
if blah then
number = number + 2^n
end if
Would it be faster to evaluate:
number = number + blah*2^n?
Which also brings the question, can you multiply a boolean value times a integer (Although I am not sure the type that is returned from 2^n, is it an integer or unsigned..etc)? (I'm working in Ada, but let's try to generalize this maybe?)
EDIT: Sorry I should clarify I am looking at 2 to the power of n, and I put c in there cause I was interested for my own learning in the future if I ever run into this problem in c and I think there are more c programmers out there on these boards then Ada (I'm assuming and you know what that means), however my current problem is in the Ada language, but the question should be fairly language independent (I hope).
There is no general answer to such a question, this depends a lot on your compiler and CPU. Modern CPU have conditional move instructions, so everything is possible.
The only ways to know here are to inspect the assembler that is produced (usually -S as compiler option) and to measure.
if we are talking about C and blah is not within your control, then just do this:
if(blah) number += (1<<n);
There is really not a boolean in C and does not need to be, false is zero and true is not zero, so you cannot assume that not zero is 1 which is what you would need for your solution, nor can you assume that any particular bit in blah is set, for example:
number += (blah&1)<<n;
Would not necessarily work either because 0x2 or 0x4 or anything non-zero with bit zero clear is considered a true. Typically you will find 0xFFF...FFFF (minus one, or all ones) used as true, but you cannot rely on typical.
Now, if you are in complete control over the value in blah, and keep it strictly to a 0 for false and 1 for true then you could do what you were asking about:
number += blah<<n;
And avoid the potential for a branch, extra cache line fill, etc.
Back to the generic case though, taking this generic solution:
unsigned int fun ( int blah, unsigned int n, unsigned int number )
{
if(blah) number += (1<<n);
return(number);
}
And compiling for the two most popular/used platforms:
testl %edi, %edi
movl %edx, %eax
je .L2
movl $1, %edx
movl %esi, %ecx
sall %cl, %edx
addl %edx, %eax
.L2:
The above uses a conditional branch.
The one below uses conditional execution, no branch, no pipeline flush, is deterministic.
cmp r0,#0
movne r3,#1
addne r2,r2,r3,asl r1
mov r0,r2
bx lr
Could have saved the mov r0,r2 instruction by re-arranging the arguments in the function call, but that is academic, you wouldnt burn a function call on this normally.
EDIT:
As suggested:
unsigned int fun ( int blah, unsigned int n, unsigned int number )
{
number += ((blah!=0)&1)<<n;
return(number);
}
subs r0, r0, #0
movne r0, #1
add r0, r2, r0, asl r1
bx lr
Certainly cheaper, and the code looks good, but I wouldnt make assumptions that the result of blah!=0, which is zero or whatever the compiler has defined as true always has the lsbit set. It doesnt have to have that bit set for the compiler to generate working code. Perhaps the standards dictate the specific value for true. by re-arranging the function parameters the if(blah) number +=... will also result in three single clock instructions and not have assumptions.
EDIT2:
Looking at what I understand to be the C99 standard:
The == (equal to) and != (not equal
to) operators are analogous to the
relational operators except for their
lower precedence. Each of the
operators yields 1 if the specified
relation is true and 0 if it is false.
Which explains why the above edit works and why you get the movne r0,#1 and not some other random number.
The poster was asking the question with regards to C but also noted that ADA was the current language, from a language independent perspective you should not assume "features" like the C feature above and use an if(blah) number = number + (1<<n). But this was asked with a C tag so the generically (processor independent) fastest result for C is, I think, number += (blah!=0)<<n; So Steven Wright's comment had it right and he should get credit for this.
The posters assumption is also basically correct, if you can get blah into a 0 or 1 form then using it in the math is faster in the sense that there is no branch. Getting it into that form without it being more expensive than a branch is the trick.
In Ada...
The original formulation:
if Blah then
Number := Number + (2 ** N);
end if;
The alternative general formulation, assuming Blah is of type Boolean and Number and N are of suitable types:
Number := Number + (Boolean'pos(Blah) * (2 ** N));
(For N and Number of user-defined integer or floating point types, suitable definitions and type conversions may be required, the key point here is the Boolean'pos() construct, which Ada guarantees will give you a 0 or 1 for the predefined Boolean type.)
As for whether this is faster or not, I concur with #Cthutu:
I would keep it with the conditional.
You shouldn't worry about low-level
optimisation details at this point.
Write the code that describes your
algorithm best and trust your
compiler.
I would keep it with the conditional. You shouldn't worry about low-level optimisation details at this point. Write the code that describes your algorithm best and trust your compiler. On some CPUs the multiplication is slower (e.g. ARM processors that have conditionals on each instruction). You could also use the ?: expression which optimises better under some compilers. For example:
number += (blah ? 2^n : 0);
If for some reason this little calculation is the bottleneck of your application after profiling then worry about low-level optimisation.
In C, regarding blah*2^n: Do you have any reason to believe that blah takes the values 0 and 1? The language only promises that 0 <-> FALSE and (everything else) <-> TRUE. C allows you to multiply a "boolean" temporary with another number, but the result is not defined except insofar as result=0 <=> the bool was false or the number was zero.
In Ada, regarding blah*2^n: The language does not define a multiplication operator on type Boolean. Thus blah cannot be a bool and be multiplied.
If your language allows multiplication between a boolean and a number, then yes, that is faster than a conditional. Conditionals require branching, which can invalidate the CPU's pipeline. Also if the branch is big enough, it can even cause a cache miss in the instructions, though that's unlikely in your small example.
Generaly, and particularly when working with Ada, you should not worry about micro-optimization issues like this. Write your code so that it is clear to a reader, and only worry about performance when you have a problem with performance, and have it tracked down to that portion of the code.
Different CPUs have different needs, and they can be insanely complex. For example, in this case which is faster depends a lot on your CPU's pipeline setup, what's in cache at the time, and how its branch prediction unit works. Part of your compiler's job is to be an expert in those things, and it will do a better job than all but the very best assembly programmers. Certianly better than you (or me).
So you just worry about writing good code, and let the compiler worry about making efficient machine code out of it.
For the problem stated, there is indeed simple expressions in C that may produce efficient code.
The nth power of 2 can be computed with the << operator as 1 << n, provided n is less than the number of value bits in an int.
If blah is a boolean, namely an int with a value of 0 or 1, your code fragment can be written:
number += blah << n;
If blah is any scalar type that can be tested for its truth value as if (blah), the expression is slightly more elaborate:
number += !!blah << n;
which is equivalent to number += (blah != 0) << n;
The test is still present but, for modern architectures, the generated code will not have any jumps, as can be verified online using Godbolt's compiler explorer.
In either case, you can't avoid a branch (internally), so don't try!
In
number = number + blah*2^n
the full expression will always have to be evaluated, unless the compiler is smart enough to stop when blah is 0. If it is, you'll get a branch if blah is 0. If it's not, you always get an expensive multiply. In case blah is false, you'll also get the unnecessary add and assignment.
In the "if then" statement, the statement will only do the add and assignment when blah is true.
In short, the answer to your question in this case is "yes".
This code shows they perform similarly, but multiplication is usually slightly faster.
#Test
public void manual_time_trial()
{
Date beforeIfElse = new Date();
if_else_test();
Date afterIfElse = new Date();
long ifElseDifference = afterIfElse.getTime() - beforeIfElse.getTime();
System.out.println("If-Else Diff: " + ifElseDifference);
Date beforeMultiplication = new Date();
multiplication_test();
Date afterMultiplication = new Date();
long multiplicationDifference = afterMultiplication.getTime() - beforeMultiplication.getTime();
System.out.println("Mult Diff : " + multiplicationDifference);
}
private static long loopFor = 100000000000L;
private static short x = 200;
private static short y = 195;
private static int z;
private static void if_else_test()
{
short diff = (short) (y - x);
for(long i = 0; i < loopFor; i++)
{
if (diff < 0)
{
z = -diff;
}
else
{
z = diff;
}
}
}
private static void multiplication_test()
{
for(long i = 0; i < loopFor; i++)
{
short diff = (short) (y - x);
z = diff * diff;
}
}