• Some users have recently had their accounts hijacked. It seems that the now defunct EVGA forums might have compromised your password there and seems many are using the same PW here. We would suggest you UPDATE YOUR PASSWORD and TURN ON 2FA for your account here to further secure it. None of the compromised accounts had 2FA turned on.
    Once you have enabled 2FA, your account will be updated soon to show a badge, letting other members know that you use 2FA to protect your account. This should be beneficial for everyone that uses FSFT.

Logic question, += operator

pr0pensity

[H]ard|Gawd
Joined
Sep 2, 2003
Messages
1,738
$x=1;
echo $x=$x+(++$x); // prints 3, as expected

$x=1;
echo $x+=++$x; // Prints 4.


$x=1;
echo $x+=$x+=++$x; // Prints 8.


I was led to believe that x+=x and x=x+x would do the same thing. Why is the variable updated before the evaluation is complete?
 
check the documentation on the order of operations. I'm guessing this is PHP, and it should be the same as c/c++, but I don't know for sure.

edit: I get 2 2 4 when I test it in c++ so I really can't comment.
 
you're using the prefix incrementer. So ++$x will increment $x by one first.

$x=1;
echo $x+=$x+=++$x; // Prints 8.


if you were to do it one operation at a time the order would look like this
$x = 1 //x = 1
++$x //x = 2
$x+=prev_result // = 2+2 so $x = 4
$x+= prev_result // = 4 + 4 so $x = 8

if you were to use $x++ (if that language has one) it would look like this
$x = 1
$x+= $x //x = 2
$x+= prev_result // 2+2 = 4
$x++ // x = 5



Makes sense?
 
Actually, if Perl (that is Perl isn't it? :p) is like C++, you're invoking undefined behavior. The C++ standard, as far as I know, doesn't define in which order operands are evaluated, so your statments (echo $x+=++$x;) and (echo $x+=$x+=++$x;) more then likely doesn't define them either. It could evaluate operand1 first, then operand2, or operand2 then operand1. It's generally (always?) a bad idea to modify the same variable more than once in single statement.

However, this is an assumption based off of a standard that isn't part of a programming language you're using. I don't know Perl though. =/
 
Jason Isom said:
The C++ standard, as far as I know, doesn't define in which order operands are evaluated, so your statments (echo $x+=++$x;) and (echo $x+=$x+=++$x;) more then likely doesn't define them either.
Seriously? I was under the impression all this was well defined, in c/c++ at least.

I'll have to break out my old college books. I have this vague impression that `=` is the lowest priority operator, everything gets higher priority.

added: Regardless, it would still be wise to break that up in a real program, so it's easily understood at a glance.
 
Just for fun I ran the same equation in Java,
x+=x+=++x; prints 4
x+=x+=x++; prints 3

I agree with the statement that you it's not good practice to modify something more than once per statement. Mostly it makes it a lot easier for somebody else reading your code to figure out what's going on. For c++ and java the compiler should already find the most efficient way of putting the code so why makes things more complex then they should be.
 
It's amazing how it seems to be different for each language. But from my experience (born and raised on C), ++$x performs to incriment operation before the rest of the statement, and $x++ performs the incrimental operation last.

EDIT
And $x++ is sooo low in C (or PHP, same thing) that it performs the operation after a comparison has executed. I.E.

Code:
$x=2;
$y=1;

if( $x > $y++ )

should produce true, and then incriment $y afterwards (didn't test this but I'm sure it works)
 
XOR != OR said:
Seriously? I was under the impression all this was well defined, in c/c++ at least.

I'll have to break out my old college books. I have this vague impression that `=` is the lowest priority operator, everything gets higher priority.

added: Regardless, it would still be wise to break that up in a real program, so it's easily understood at a glance.

I said operands, not operators. If
Code:
x + a
is defined as
Code:
+(x, a)
I distinctly remember reading that you don't know if x is evaluated first, or a. I may be mistaken, because it's been awhile since I read that...I tried searching on parashift but couldn't come up with anything.


EDIT

To elaborate:
Code:
x + ++x
is defined as
Code:
+(x, ++x)
Lets say x = 5

Now it's undefined because if the first parameter is evaluated first, then the first parameter is 5, and the second is 6. If the second parameter is evaluated first then the first pameter is 6, and the second parameter is 6.

Now, I really really hope I'm not talking out of my ass...if I am, please someone correct me and I'll delete this post. :) Until then I'll continue searching for some sort of credible source.
 
i agree ^

if the operand precedence remained the same for $x = $x+$x and $x += $x i dont see why $x = $x + (++$x) and $x += ++$x would yield 2 different answers, because evaluated they should both appear as +($x, ++$x) yeilding the same answer, which is not the case.

pre-increment behavior/precedence doesn't really matter, relatively speaking, since it's effects should theoretically be the same with either $x += ++$x or $x = $x + (++$x), since both should be parsed as +($x, ++$x). unless of course the operand precedence is in fact different depending on the syntax you choose, as mentioned by the above poster.
 
I'm thinking this is a case where "don't do that" is probably the best response.
 
pr0pensity said:
$x=1;
echo $x=$x+(++$x); // prints 3, as expected

$x=1;
echo $x+=++$x; // Prints 4.

$x=1;
echo $x+=$x+=++$x; // Prints 8.
...

pr0pensity said:
Why doesn't x = x + x the same thing as x += x? How does it work internally?

Taking a guess:

$x=$x+(++$x) breaks down as:
1. assign to the address of $x
2. the current value of $x //1
3. plus
4. (preincrement the value at $x)
5. the current value of $x //2, courtesy of the above step
6. assignment of 1 + 2 made to address of $x

$x+=++$x breaks down as:
1. assign to the address of $x
2. (preincrement the value at $x)
3. the current value of $x //2, courtesy of the above step
4. plus
5. the current value of $x //2, courtesy of step 2
6. assignment of 2 + 2 made to address of $x

It appears that $x+=[whatever] evaluates as $x = [whatever]+$x and that preincrementation happens just before the value of the variable preincremented is used AT the place where it is preincremented.

Give $x=(++$x)+$x a try. If I'm guessing correctly, the result will be 4, breaking down exactly the same as $x+=++$x.

Give $x=$x+(++$x)+$x a try. If I'm guessing correctly, the result will be 5. Also, try $x+=$x+(++$x). If I'm guessing correctly, the result will also be 5. (In both cases: 1; increment; plus 2; plus 2; assign.)

At any rate, it isn't behaving how I'd expect, but only because I'd expect preincrement to happen before ALL else, such that $x=$x+(++$x); // prints 4.

I'll concur with "it's not good practice to modify something more than once per statement." Overall, being concise isn't the be all, end all. Clear beats concise when it comes down to debugging, or modification in general.
 
MovieMan80 said:
Just for fun I ran the same equation in Java,
x+=x+=++x; prints 4
x+=x+=x++; prints 3

...

It appears that Java treats += as:
Take value of LHS
Evaluate RHS
Add
Assign to LHS

Whereas the other language treats += as:
Evaluate RHS
Take value of LHS
Add
Assign to LHS
 
Using VC++ 6.0:

Code:
x= 1;
cout<< "(x=x+(++x)) = " << (x=x+(++x)) << endl;
x = 1;
cout<< "(x+=(++x)) = " << (x+=(++x)) << endl;
x = 1;
cout<< "(x+=x+=(++x)) = " << (x+=x+=(++x)) << endl;

Yields:
(x=x+(++x)) = 4
(x+=(++x)) = 4
(x+=x+=(++x)) = 8

Those were the results I'd expected.
 
And:
(x+=(++x)+(x+=(++x)+x)) = 27
((++x)+(x+=(++x)+x)) = 18
((x+=(++x)+x)) = 6

So it appears that VC++ 6.0 also treats += as:
Evaluate RHS
Take value of LHS
Add
Assign to LHS

However, it appears VC++ 6.0 does preincrements absolutely first, rather than "just before the value of the variable preincremented is used AT the place where it is preincremented."

(x+=(++x)+(x+=(++x)+x)) = 9+(9)+(3+((3)+3)) = 27
 
It depends when it saves the result of x back. Things differ between languages and compilers.
 
Code:
#include <stdio.h>

void do1() {
	int x = 1;
	x = x+(++x);
	printf("Result 1: %i\n", x);
}

void do2() {
	int x = 1;
	x += ++x;
	printf("Result 2: %i\n", x);
}

void do3() {
	int x = 1;
	x += x += ++x;
	printf("Result 3: %i\n", x);
}

int main(int argc, const char * argv[]) {
	do1();
	do2();
	do3();
}

harry@bluester:~/src$ gcc -g -otest1 test1.c
harry@bluester:~/src$ ./test1
Result 1: 4
Result 2: 4
Result 3: 8

Propensity I get a different answer for the first one.

The asm for the first one:

Code:
do1:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$24, %esp
	movl	$1, -4(%ebp)
	leal	-4(%ebp), %eax
	incl	(%eax)
	movl	-4(%ebp), %edx
	leal	-4(%ebp), %eax
	addl	%edx, (%eax)
	movl	-4(%ebp), %eax
	movl	%eax, 4(%esp)
	movl	$.LC0, (%esp)
	call	printf
	leave
	ret
	.size	do1, .-do1
	.section	.rodata

The asm for the second one:

Code:
do2:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$24, %esp
	movl	$1, -4(%ebp)
	leal	-4(%ebp), %eax
	incl	(%eax)
	movl	-4(%ebp), %edx
	leal	-4(%ebp), %eax
	addl	%edx, (%eax)
	movl	-4(%ebp), %eax
	movl	%eax, 4(%esp)
	movl	$.LC1, (%esp)
	call	printf
	leave
	ret
	.size	do2, .-do2
	.section	.rodata

each time its doing x++ and saving it back to mem:
leal -4(%ebp), %eax
incl (%eax)
In fact, it did not even alloc a register for it. It just loaded its address in eax.
(Looking at the asm above it could be made more efficient)

Anyway, its going to differ across languages, compilers.
I would be intersted to see how it compilers under windows if anyone would do that. :)
 
VC++ 6.0 disassembly of a debug build, also very sub-optimal. Note that (x=x+(++x)) and
(x+=(++x)) disassemble exactly the same.
Code:
13:   cout<< "(x=x+(++x)) = " << (x=x+(++x)) << endl;
0041156F   push        offset @ILT+10(endl) (0040100f)
00411574   mov         eax,dword ptr [ebp-4]
00411577   add         eax,1
0041157A   mov         dword ptr [ebp-4],eax
0041157D   mov         ecx,dword ptr [ebp-4]
00411580   add         ecx,dword ptr [ebp-4]
00411583   mov         dword ptr [ebp-4],ecx
...
16:   cout<< "(x+=(++x)) = " << (x+=(++x)) << endl;
004115AE   push        offset @ILT+10(endl) (0040100f)
004115B3   mov         eax,dword ptr [ebp-4]
004115B6   add         eax,1
004115B9   mov         dword ptr [ebp-4],eax
004115BC   mov         ecx,dword ptr [ebp-4]
004115BF   add         ecx,dword ptr [ebp-4]
004115C2   mov         dword ptr [ebp-4],ecx
...
19:   cout<< "(x+=x+=(++x)) = " << (x+=x+=(++x)) << endl;
004115ED   push        offset @ILT+10(endl) (0040100f)
004115F2   mov         eax,dword ptr [ebp-4]
004115F5   add         eax,1
004115F8   mov         dword ptr [ebp-4],eax
004115FB   mov         ecx,dword ptr [ebp-4]
004115FE   add         ecx,dword ptr [ebp-4]
00411601   mov         dword ptr [ebp-4],ecx
00411604   mov         edx,dword ptr [ebp-4]
00411607   add         edx,dword ptr [ebp-4]
0041160A   mov         dword ptr [ebp-4],edx
...
22:   cout<< "((x+=(++x)+x)) = " << ((x+=(++x)+x)) << endl;
00411635   push        offset @ILT+10(endl) (0040100f)
0041163A   mov         ecx,dword ptr [ebp-4]
0041163D   add         ecx,1
00411640   mov         dword ptr [ebp-4],ecx
00411643   mov         edx,dword ptr [ebp-4]
00411646   add         edx,dword ptr [ebp-4]
00411649   mov         eax,dword ptr [ebp-4]
0041164C   add         eax,edx
0041164E   mov         dword ptr [ebp-4],eax
...
25:   cout<< "((++x)+(x+=(++x)+x)) = " << ((++x)+(x+=(++x)+x)) << endl;
00411679   push        offset @ILT+10(endl) (0040100f)
0041167E   mov         edx,dword ptr [ebp-4]
00411681   add         edx,1
00411684   mov         dword ptr [ebp-4],edx
00411687   mov         eax,dword ptr [ebp-4]
0041168A   add         eax,1
0041168D   mov         dword ptr [ebp-4],eax
00411690   mov         ecx,dword ptr [ebp-4]
00411693   add         ecx,dword ptr [ebp-4]
00411696   mov         edx,dword ptr [ebp-4]
00411699   add         edx,ecx
0041169B   mov         dword ptr [ebp-4],edx
...
28:   cout<< "(x+=(++x)+(x+=(++x)+x)) = " << (x+=(++x)+(x+=(++x)+x)) << endl;
004116C9   push        offset @ILT+10(endl) (0040100f)
004116CE   mov         ecx,dword ptr [ebp-4]
004116D1   add         ecx,1
004116D4   mov         dword ptr [ebp-4],ecx
004116D7   mov         edx,dword ptr [ebp-4]
004116DA   add         edx,1
004116DD   mov         dword ptr [ebp-4],edx
004116E0   mov         eax,dword ptr [ebp-4]
004116E3   add         eax,dword ptr [ebp-4]
004116E6   mov         ecx,dword ptr [ebp-4]
004116E9   add         ecx,eax
004116EB   mov         dword ptr [ebp-4],ecx
004116EE   mov         edx,dword ptr [ebp-4]
004116F1   add         edx,dword ptr [ebp-4]
004116F4   mov         eax,dword ptr [ebp-4]
004116F7   add         eax,edx
004116F9   mov         dword ptr [ebp-4],eax
...
 
Thanks Cardboard Hammer. On a side note its such a shame at how inefficient these disamblies are. It would be nice if these compilers would have allociated a register for these variables. It seems like they love to access memory instead of work on registers.
 
Your disassemblies are inefficient because both of you are producing debug builds.Also it's kind of pointless to try to derive any conclusions from assemblies produced by using code that produces undefined results for the very reason that it's undefined.

At best you can compare code generated by a specific compiler, because the results you are getting are going to be compiler dependent anyways.
 
Jason, i'm not sure what you mean. I know there are difference between languages and compilers. I just wanted to see if my compiler implemented the two different commands in different ways. Looking the the asm tells me how my compiler did something. as i'm sure you know, you can implement things (such as loops) in asm many different ways.
 
zappa86 said:
Jason, i'm not sure what you mean. I know there are difference between languages and compilers. I just wanted to see if my compiler implemented the two different commands in different ways. Looking the the asm tells me how my compiler did something. as i'm sure you know, you can implement things (such as loops) in asm many different ways.

[39.15] Why do some people think x = ++y + y++ is bad?
Because it's undefined behavior, which means the runtime system is allowed to do weird or even bizarre things.

The C++ language says you cannot modify a variable more than once between sequence points. Quoth the standard (section 5, paragraph 4):

This means that the standard doesn't define what's supposed to happen when you use commands like this. Meaning compiler programmers have no guideline on what to do in these situations, so while one manufacture may decide that you should evaluate it one way, another compiler may decide on a completely different approach. And it would be horrible to try and draw any conclusions from the results because the results will most certainly vary.

At any rate

ameoba said:
I'm thinking this is a case where "don't do that" is probably the best response.
 
zappa86 said:
Jason, i'm not sure what you mean. I know there are difference between languages and compilers. I just wanted to see if my compiler implemented the two different commands in different ways. Looking the the asm tells me how my compiler did something. as i'm sure you know, you can implement things (such as loops) in asm many different ways.

What Jason is trying to tell you is that both you and Cardboard Hammer didn't ask the compiler to do any optimizations. This is typical of debug builds, which are built very quickly (again and again, to optimize a developer's edit-compile-debug workflow loop) and which need to line-up object code with source lines very reliably.

In a release build with VC++ 6.0, the two functions are heavily optimized; they don't do any math at all, since the compiler can, at compile time, figure out what the result of the epxressions are and leave them as a constant. If I build your code with this command line in VC++ 6.0, Zappa:

Code:
cl /FAsc /c /Ox source.cpp

I get this code for the main function:

Code:
PUBLIC	_main
; Function compile flags: /Ogty
_TEXT	SEGMENT
_argc$ = 8						; size = 4
_argv$ = 12						; size = 4
_main	PROC NEAR

; 22   : 	do1();

  00030	6a 04		 push	 4
  00032	68 00 00 00 00	 push	 OFFSET FLAT:$SG609
  00037	e8 00 00 00 00	 call	 _printf

; 23   : 	do2();

  0003c	6a 04		 push	 4
  0003e	68 00 00 00 00	 push	 OFFSET FLAT:$SG613
  00043	e8 00 00 00 00	 call	 _printf

; 24   : 	do3();

  00048	6a 08		 push	 8
  0004a	68 00 00 00 00	 push	 OFFSET FLAT:$SG617
  0004f	e8 00 00 00 00	 call	 _printf
  00054	83 c4 18	 add	 esp, 24			; 00000018H

; 25   : }

  00057	33 c0		 xor	 eax, eax
  00059	c3		 ret	 0
_main	ENDP
_TEXT	ENDS
END

The compiler realizes that the do functions are trivial, so it doesn't call them. Instead, it inlines them. It has discovered that the variable x in each one is local to the function, and only used for some very simple math. It does the math, gets a result, and doesn't allocate storage for the varialbe. As such, it's able to push an immediate argument constant onto the stack and offer it to printf().

The reason you're disappointed in the efficiency of the emitted code is that you haven't asked for the emitted code to be made optimal.

BTW, Zappa, what processor architecture are you targeting? What uses Intel register names, but doesn't use Intel opcodes or Intel assembler syntax?
 
I used a debug build because a release build optimizes the math out entirely, as you've shown. I didn't expect a debug build to be particularly optimal; I was just noting that it was far from it.

I do wonder why the C++ standard doesn't bother to define what happens. As much as one SHOULDN'T do it, even if it were defined, I wouldn't expect it to be that difficult to make the definition.
 
That was AT&T style for 80x86 (run on my p4 prescott thats overheating right now). I dont do the whole NASM and vc++, but I should learn it more. I do the gas (assembler) and gcc (compiler). So:

mov eax, edx
is
movl %edx, %eax

or

mov eax, [esp+4]
is
movl 4(%esp), eax

Thanks. for the vc++ advice on optimization. I'm going to do some win programming eventually so its better to know how to optimize it there. I just realized I had to set my CFLAGS differently to get the optimization with gcc.

http://en.wikipedia.org/wiki/GNU_assembler
 
zappa86 said:
That was AT&T style for 80x86 (run on my p4 prescott thats overheating right now).

Thanks for the info ... but, AT+T? What do they have to do with the x86 processors?

The optimizations shouldn't be unique to VC++. I'd expect any competant, commercial compiler to produce the same optimizations for that source file.
 
Cardboard Hammer said:
It appears that Java treats += as:
Take value of LHS
Evaluate RHS
Add
Assign to LHS

Whereas the other language treats += as:
Evaluate RHS
Take value of LHS
Add
Assign to LHS

Interesting, I'll be sure to use this fact to abuse my students if I ever become a Java Teacher ;)
 
mikeblas said:
Thanks for the info ... but, AT+T? What do they have to do with the x86 processors?

Nothing, but AT&T originally wrote Unix, C and the rest of the basic dev tools for Unix The AT&T asm syntax is the one that they used (and as a result is traditional for unix tools.).
 
Back
Top