int x = 10;
x += x--;
In C#/.Net, why does it equal what it equals? (I'm purposely leaving the answer out so you can guess and see if you're right)
Look at this statement:
x += x--;
This is equivalent to:
x = x + x--;
Which is equivalent to:
int a1 = x; // a1 = 10, x = 10
int a2 = x--; // a2 = 10, x = 9
x = a1 + a2; // x = 20
So x is 20 afterwards - and that's guaranteed by the spec.
What's also pretty much guaranteed, although not by the spec, is that anyone using such code will be attacked by their colleagues. Yes, it's good that the result is predictable. No, it's not good to use that kind of code.
Jon is of course right.
A good way to think about this is to remember:
1) subexpressions are always evaluated left to right. Period. Evaluation of a subexpression may induce a side effect.
2) execution of operators is always done in the order indicated by parentheses, precedence and associativity. Execution of operators may induce a side effect.
The "x" to the left of the += is the leftmost subexpression, and therefore rule (1) applies. Its value is computed first -- 10.
The x-- to the right of the += is the next one in left-to-right order, so it is evaluated next. The value of x-- is 10, and the side effect is that x becomes 9. This is as it should be, because -- is of higher precedence than +=, so its side effect runs first.
Finally, the side effect of += runs last. The two operands were 10 and 10, so the result is to assign 20 to x.
I get questions about this all the time. Remember, the rules are very straightforward: subexpressions left-to-right, operators in precedence order, period.
In particular, note that the commonly-stated reasoning "the -- operator is postfix and therefore runs after everything else" is incorrect reasoning. I discuss why that is incorrect in the articles below.
Here are some articles I've written on this subject:
http://blogs.msdn.com/ericlippert/archive/tags/precedence/default.aspx
20; the "--" doesn't happen until after everything gets evaluated, and that value is overwritten by the left-hand side of the equals.
From the specs 7.13.2
If the return type of the selected operator is implicitly convertible to the type of x, the operation is evaluated as x = x op y, except that x is evaluated only once.
So your statement is equivalent to x = x + x--; which is evaluated in left-to-right order and gives the answer 20.
Note that there is also a difference between --x and x-- here. If you had written x += --x; this would be equivalent to x = x + --x; then you would get 19. This is because the value of x is decremented and the resulting value is used in the expression (unlike x-- where the original value of x is used in the expression).
This expression x = x + --x + x will give 28 because the third timefourth time (see comments) x is evaluated it is 9.
The answer is 20. And Tom, you really aren't as surprised as your questions seems to imply, right? And to those of you who are assuming the answer is 19 - I think you're confused with x += --x;
Related
I don't understand why in C#, ++x - --x is 1 for every value of x.
int x = 0;
Console.WriteLine(++x - --x); // gives 1
I believe the answer should be 0, as variable x involves both sides. And x is incremented and decrimented before the processing of substraction. The same equation in C++ returns 0, that make sense to me.
Can anyone give a clue what's actually happening here?
This is actually undefined behaviour in C and C++. For example, C states that reading and modifying an object without an intervening sequence point is undefined.
However, C# locks down the behaviour so that behaviour is defined. From the online docs:
Unrelated to operator precedence and associativity, operands in an expression are evaluated from left to right. The following examples demonstrate the order in which operators and operands are evaluated:
OPERAND EVALUATION
Expression Order of evaluation
a + b a, b, +
a + b * c a, b, c, *, +
a / b + c * d a, b, /, c, d, *, +
a / (b + c) * d a, b, c, +, /, d, *
And, from the operator precedence table, you can see that ++x (unary category) has a higher precedence than x + y (additive category).
In your case, the sub-expression ++x is evaluated first and results in x being pre-incremented so the value of that sub-expression is the original x plus one.
Then the sub-expression --x is evaluated and results in x (already incremented from the previous step) being pre-decremented so the value of that sub-expression is the original x.
And, since x + 1 - x == 1 for all but the darkest corners of the math universe, the result is always one. I still wouldn't do something like this, but it at least is well defined.
var distances = new Dictionary<char, float>();
var nodes = new List<char>();
I have this line to find the smallest distance
nodes.Sort((x, y) => distances[x] - distances[y]);
When I use int it works well, but when I used float I got a message
cannot convert lambda expression to type
'System.Collections.Generic.IComparer' because it is not a
delegate type
Do you have an idea?
First off, your original program is a bad programming practice when the values are integers. It works for chars, but I would avoid this bad programming practice.
The delegate you pass to the sort function must have a number of properties; in particular it must return a negative int if x is smaller than y, a positive int if x is greater than y, and zero if they are equal. Your original lambda does not do that for integer values. (See if you can find two integers x and y such that x is smaller than y but x - y is positive.)
The delegate must also impose a total order. In a total order:
Transitivity must hold. If A == B and B == C then A must equal C. If A < B and B < C then A must be smaller than C. And so on.
It must be antisymmetric. That is, if A < B then B > A, and so on.
Subtraction does not meet these conditions in integers. The correct code is to actually write a comparison.
nodes.Sort((x, y) => x < y ? -1 : (x > y ? 1 : 0));
That then works well for chars and floats, provided there are no NaNs. If you have NaNs then you need to do extra work to impose a total order.
I would also point out that this ordinal comparison on chars is usually not the comparison you want. Sure, this will correctly note that e is smaller than z, but simple ordinal comparison also says that z is smaller than é, which is likely not what you want. Character ordering depends on culture; are you sure you want to order by the order that the Unicode committee just happened to impose?
For more on this topic see my series of articles; it begins here:
http://ericlippert.com/2011/01/20/bad-comparisons-part-one/
You can't convert your lambda expression into a Comparison<char> (which is what you want) because it returns a float - you've effectively got a Func<char, char, float> there, whereas Comparison<char> is closer to Func<char, char, int>.
The simplest approach is to use float.CompareTo:
nodes.Sort((x, y) => distances[x].CompareTo(distances[y]));
Or if you don't need to sort in-place, you could use LINQ:
var sorted = nodes.OrderBy(x => distances[x]);
Reformulate the lambda expression as follows.
nodes.Sort((x, y) =>
{
float Result = distances[x] - distances[y];
if ( Result > 0.0f )
return 1;
else if ( Result < 0.0f )
return -1;
else
return 0;
});
Ive seen the following syntax in a couple of code samples, it looks pretty obvious, but not in the context that i saw it in,
so can someone confirm
var x = 1
var y = 2
var z = 3
x = y = z
so in essence does this mean that
x and y both equal z?
tried to google this, and couldn't find the syntax, also looked in murach .net books with no luck
The assignment operator returns the value being assigned as it's result
The assignment operator (=) stores the value of its right-hand operand in the storage location, property, or indexer denoted by its left-hand operand and returns the value as its result.
So
x = y = z;
Is parsed as
x = (y = z);
And this is equivalent to
y = z;
x = y;
In the end, x, and y are assigned the same value as z (in this case, 3).
This will indeed set x and y to the value of z, however to see why you can parenthesize the expression:
x = (y = z);
In this case the y = z expression will return the new value for y (in this case 3), and set x to that value.
This works for very long expressions too:
x = y = z = a = b
x = (y = (z = (a = b)))
Where x, y, z and a will be set to the value of b.
EDIT:
Also remember the order of precedence of operators when using this method of reading into a long expression, while its not a good practice to create huge chained statements you do sometimes see it in code online, another (and better) way you can use to read confusing statements like this is to think of the syntax tree that is generated.
Ehm, yes that is correct :).
x, y and x will all be 3 as you can assign values like that.
you can make the chain as long as you like too.
a = b = c = d;
all preceding values would be the value of d
Probably a silly question, since I may have already answered my question, but I just want to be sure that I'm not missing something
Constant expressions are evaluated at compile time within checked context. I thought the following expression shouldn't be evaluated at compile time, since I assumed C# considers a particular expression as a constant expression only if all operands on the left-hand side are constants:
int i= 100;
long u = (int.MaxValue + 100 + i); //error
Instead it appears compiler considers any subexpression where both operands are constants as a constant expression, even if other operands in an expression are non-constants? Thus compiler may only evaluate a part of an expression at compile time, while the remained of the expression ( which contains non-constant values ) will get evaluated at run-time --> I assume in the following example only (200 +100) gets evaluated at compile time
int i=100;
long l = int.MaxValue + i + ( 200 + 100 ); // works
Are my assumptions correct?
thanx
doec C# classify any subexpression where both operands are constants as a constant expression, even if other operands in an expression are non-constants?
The answer to your question is "yes," but it is important to clearly understand what constitutes a "subexpression".
I assume in "int.MaxValue + i + ( 200 + 100 )" only (200 + 100) gets evaluated at compile time
Correct. Now, if you had said instead "int.MaxValue + i + 200 + 100" then "200 + 100" would never be evaluated at all because that's not a subexpression due to associativity.
This is a bit subtle. Let me explain in detail.
First off, let's distinguish between de jure compile time constants and de facto compile time constants. Let me give you an example. Consider this method body:
const int x = 123;
int y = x + x;
if (y * 0 != 0) Console.WriteLine("WOO HOO");
M(ref y);
In C# 1 and 2 if you compiled that with optimizations on, that would be compiled as if you had written:
int y = 246;
M(ref y);
The constant x vanishes, y is initialized to the constant-folded expression, and the arithmetic optimizer realizes that any local integer times zero is never not equal to zero, so it optimizes that away as well.
In C# 3 I accidentally introduced a bug in the optimizer which was not fixed in C# 4 either. In C# 3/4 we'd generate that as
int y = 246;
bool b = false;
if (b) Console.WriteLine("WOO HOO");
M(ref y);
That is, the arithmetic is optimized away, but we don't go the step further and optimize away the "if(false)".
The difference is that the constant folding behaviour on x + x is guaranteed to happen at compile time, but the constant folding behaviour on the partially-variable expression y*0 is not.
I regret the error and apologize. However, the reason I changed this in C# 3 and accidentally introduced a bug in the code generator was to fix a bug in the semantic analyzer. In C# 2.0 this was legal:
int z;
int y = 246;
if (y * 0 == 0) z = 123;
Console.WriteLine(z);
That should not be legal. You know and I know that y * 0 == 0 will always be true, and therefore z is assigned before it is read, but the spec says that this analysis must only be performed if the expression in the "if" is a compile-time constant, and that's not a compile-time constant because it contains a variable. We took the breaking change for C# 3.0.
So, OK, let's assume that you understand the difference between a de jure constant that must be evaluated because the spec says so, and a de facto constant that is evaluated because the optimizer is smart. Your question is under what circumstances can an expression be de jure and de facto partially "folded" at compile time? (By "folded" I mean resolving an expression containing constants into a simpler expression.)
The first thing we have to consider is the associativity of the operators. Only once we have done the associativity and precedence analysis do we know what is and is not a subexpression. Consider
int y = 3;
int x = 2 - 1 + y;
Addition and subtraction are left-associative, as are most operators in C#, so that is the same as
int x = (2 - 1) + y;
Now clearly (2 - 1) is a de jure constant expression, so that is folded and becomes 1.
If on the other hand you said
int x = y + 2 - 1;
That is
int x = (y + 2) - 1;
and it is not folded because those are two non-constant expressions.
This could have a real effect in a checked context. If y is int.MaxValue - 1 then the first version will not overflow but the second version will!
Now, the compiler and optimizer are permitted to say "well, I happen to know that this is an unchecked context, and I happen to know that I can turn that into "y + 1" safely, so I will." The C# compiler does not do this but the jitter might. In your particular example,
long l = int.MaxValue + i + 200 + 100 ;
is actually code-gen'd by the C# compiler as
long l = ((int.MaxValue + i) + 200) + 100 ;
And not
long l = (int.MaxValue + i) + 300;
The jitter may choose to make that optimization if it so wishes and can prove that doing so is safe.
But
long l = int.MaxValue + i + (200 + 100);
would of course be generated as
long l = (int.MaxValue + i) + 300;
However, we do perform the optimization you want on strings in the C# compiler! If you say
string y = whatever;
string x = y + "A" + "B" + "C";
then you might think, well, that's a left-associative expression so that's:
string x = ((y + "A") + "B") + "C";
And therefore there will be no constant folding. However, we actually detect this situation and at compile time do the folding to the right, so we generate this as if you'd written
string x = y + "ABC";
Thereby saving the cost of the concatenations at runtime. The string concat optimizer is actually reasonably sophisticated about recognizing various patterns for gluing strings together at compile time.
We could do the same for unchecked arithmetic. We just haven't gotten around to it.
void swap(ref int x, ref int y)
{ x = x ^ y; y = y ^ x; x = x ^ y; }
im learning about bitwise XOR. how is this swapping occurring? it's blowing my mind. this method is supposed to swap the contents of X and Y, but i dont understand AT ALL what is going on?
Beware. This method has a very very nasty property. It was used in one of the Underhanded C Contest entries. It will work happily... until one day someone tries something like swapping two array elements a[i] and a[j] without assuring that i!=j.
When both references refer to the same variable, this method zeroes it.
Wikipedia has an excellent explanation of the Swap-By-XOR algorithm.
The formal proof that this algorithm works is a bit involved, and requires the use of the mathematical properties of binary numbers.
But in simplified form, we can consider each bit of the binary value separate, since the XOR operation acts on each independently. As such, it's sufficient to show that this works with 1-bit values, since by induction we can demonstrate it works for any length binary value. It's quite simple to build an appropriate truth table for these operations, so I leave that out.
Swap by XOR is not the only "creative" swapping algorithm possible. A similar result can be achieved by using arithmetic operations:
void Swap( ref int x, ref int y )
{
x = x + y;
y = x - y;
x = x - y;
}
From a practical perspective, this is a technique that should be avoided in most cases. As you yourself recognize, the logic of this method is not immediately obvious and it can lead to maintainability and extensibility problems ... not the least of which is the fact that Swap( ref x, ref x ) will NOT do what the method's name implies (it will zero out the value, in fact).
Just look at it one bit at a time and one step at a time.
x|y -> x = x ^ y x|y -> y = y ^ x x|y -> x = x ^ x x|y
0|0 0|0 0|0 0|0
0|1 1|1 1|0 1|0
1|0 1|0 1|1 0|1
1|1 0|1 0|1 1|1
Here you can clearly see that the result in each case is a swapping of the bits. From here it is clear why it works in the general case. More formal explanations are possible.
Anytime you find yourself saying "I don't understand at all what is going on" is a strong indication that you should find a clearer way to write semantically-equivalent code.
swap() can be implemented with a single CPU instruction on most modern architectures (including i686 and x86_64). Hence, you are better off writing it in a way the compiler can recognize and convert accordingly rather than trying to micro optimize in a way that will actually make your code slower and less readable.
First of all look at how XOR works:
a | b | a^b
- - - - - -
0 | 0 | 0
1 | 0 | 1
0 | 1 | 1
1 | 1 | 0
So the result of an xor operation is 1 or true if the inputs are different, and 0 if the inputs are equal. Another way to look at it is to think of it as addition without carry, i'll denote it as (+) :
x = x ^ y <=> x = x (+) y;
y = y ^ x <=> y = y (+) x;
x = x ^ y <=> x = x (+) y;
First step : set all the bits that are 0 in x but 1 in y to 1. Now some bits are wrong in x because if a bit is 1 in x and also y it'll be set to 0. The other bits are correct.
Second step : Now set the same bit positions we set to 1 in x to 0 in y (we are done with y now). This works because x already has all bits that are different set to 1, so XORing y with x now basically means: toggle the bits in y that are set to 1 in x. We are done with y now :)
Third step : now we still need to set those bits in x to 1 that already were set to 1 originally and were reset to 0 after the first step back to 1. How ? We just XOR x with y one last time because what does xor do ? it sets a bit to 1 if the 2 inputs differ, which is exactly what we need.
If you are still confused about it you should just draw it on paper and play it through to see how it works and/or refer to the tables Jason did above.