Take the following C++14 code snippet:
unsigned int f(unsigned int a, unsigned int b){
if(a>b)return a;
return b;
}
Statement: the function f returns the maximum of its arguments.
Now, the statement is "obviously" true, yet I failed to prove it rigorously with respect to the ISO/IEC 14882:2014(E) specification.
First: I cannot state the property in a formal way.
A formalized version could be:
For every statement s, when the abstract machine (which is defined in the spec.) is in state P and s looks like "f(expr_a,expr_b)" and 'f' in s is resolved to the function in question, s(P).return=max(expr_a(P).return, expr_b(P).return).
Here for a state P and expression s, s(P) is the state of the machine after evaluation of s.
Question: What would be a correctly formalized version of the statement? How to prove the statement using the properties imposed by the above mentioned specification? For each deductive step please reference the applicable snippet from the standard allowing said step (the number of the segment is enough).
Edit: Maybe formalized in Coq
Please appologize for my approximate ageing mathematic knowledge.
Maximum for a closed subset of natural number (BN) can be defined as follow:
Max:(BN,BN) -> BN
(x ∊ BN)(a ∊ BN)(b ∊ BN)(x = Max(a,b)) => ( x=a & a>b | x=b )
where the symbol have the common mathemical signification.
While your function could be rewritten as follow, where UN is the ensemble of unsigned int:
f:(UN,UN) -> UN
(x ∊ UN)(a ∊ UN)(b ∊ UN)(x = f(a,b)) => ( x=a && a>b || x=b )
Where symbol = is operator==(unsigned int,unsigned int), etc...
So the question reduces to know if the standard specifies that the mathematical structure(s) formed by the unsigned integer with C++ arithmetic operators and comparison operator is isomorphic to the matematical structures (classes,categories) formed by a closed subset of N with the common arithemtic operation and relations. I think the answer is yes, this is expressed in plain english:
C++14 standard,[expr.rel]/5 (Relational Operators)
If both operands (after conversions) are of arithmetic or enumeration type, each of the operators shall yield true if the specified relationship is true and false if it is false.
C++14 standard, [basic.fundamental]/4 (Fundamental Types)
Unsigned integers shall obey the laws of arithmetic modulo 2n where n is the number of bits in the value representation of that particular size of integer.
Then you could also proove that ({true,false},&&,||) is also isomorphic to boolean arithmetic by analysing the text in [expr.log.and]
and [expr.log.or]
I do not think that you should go further than showing that there is this isomorphism because further would mean demonstrating axioms.
It appears to me that the easiest solution is to prove this backwards. If the first argument to f is the maximum argument, prove that the first argument is returned (fairly easy - the maximum argument a is by definition bigger than b). If the second argument is the maximum argument, prove that the second argument is returned. If the two are equal, show that there is no unique maximum element, so the second argument is still a maximum argument.
Finally, prove that these three options are exhaustive. If a unique maximum argument is passed, it must be passed either as the first or the second argument, since f is binary.
I am unsure about what you want. Looking at a previous version, N3337, we can easily see that almost everything is specified:
a and b starts with the calling values (Function 5.2.2 - 4)
Calling a function executes the compound statement of the function body (Obvious, but where?)
The statements are normally executed in order (Statements 6)
If-statements execute the first sub-statement if condition is true (The If Statement 6.4.1)
Relations actually work as expected (Relation operators 5.9 - 5)
The return-statement returns the value to the caller of the function (The return statement 6.6.3)
However, you attempt to start with f(expr_a, expr_b); and evaluating the arguments to f potentially requires a lot more; especially since they are not sequenced - and could be any function-call.
Related
Context
While I was reading Consistent comparison, I have noticed a peculiar usage of the verb to compare:
There’s a new three-way comparison operator, <=>. The expression a <=> b
returns an object that compares <0 if a < b, compares >0 if a > b, and
compares ==0 if a and b are equal/equivalent.
Another example found on the internet (emphasis mine):
It returns a value that compares less than zero on failure. Otherwise,
the returned value can be used as the first argument on a later call
to get.
One last example, found in a on GitHub (emphasis mine):
// Perform a circular 16 bit compare.
// If the distance between the two numbers is larger than 32767,
// and the numbers are larger than 32768, subtract 65536
// Thus, 65535 compares less than 0, but greater than 65534
// This handles the 65535->0 wrap around case correctly
Of course, for experienced programmers the meaning is clear. But the way the verb to compare is used in these examples is not standard in any standardized forms of English.
Questions*
How does the programming jargon sentence "The object compares less than zero" translate into plain English?
Does it mean that if the object is compared with0 the result will be "less than zero"?
Why would be wrong to say "object is less than zero" instead of "object compares less than zero"?
* I asked for help on English Language Learners and English Language & Usage.
"compares <0" in plain English is "compares less than zero".
This is a common shorthand, I believe.
So to apply this onto the entire sentence gives:
The expression a <=> b returns an object that compares less than zero
if a is less than b, compares greater than zero if a is greater than
b, and compares equal to zero if a and b are equal/equivalent.
Which is quite a mouthful. I can see why the authors would choose to use symbols.
What I am interested in, more exactly, is an equivalent expression of "compares <0". Does "compares <0" mean "evaluates to a negative number"?
First, we need to understand the difference between what you quoted and actual wording for the standard. What you quoted was just an explanation for what would actually get put into the standard.
The standard wording in P0515 for the language feature operator<=> is that it returns one of 5 possible types. Those types are defined by the library wording in P0768.
Those types are not integers. Or even enumerations. They are class types. Which means they have exactly and only the operations that the library defines for them. And the library wording is very specific about them:
The comparison category types’ relational and equality friend functions are specified with an anonymous parameter of unspecified type. This type shall be selected by the implementation such that these parameters can accept literal 0
as a corresponding argument. [Example: nullptr_t satisfies this requirement. —
end example] In this context, the behaviour of a program that supplies an argument other than a literal 0 is undefined.
Therefore, Herb's text is translated directly into standard wording: it compares less than 0. No more, no less. Not "is a negative number"; it's a value type where the only thing you can do with it is comparing it to zero.
It's important to note how Herb's descriptive text "compares less than 0" translates to the actual standard text. The standard text in P0515 makes it clear that the result of 1 <=> 2 is strong_order::less. And the standard text in P0768 tells us that strong_order::less < 0 is true.
But it also tells us that all other comparisons are the functional equivalent of the descriptive phrase "compares less than 0".
For example, if -1 "compares less than 0", then that would also imply that it does not compare equal to zero. And that it does not compare greater than 0. It also implies that 0 does not compare less than -1. And so on.
P0768 tells us that the relationship between strong_order::less and the literal 0 fits all of the implications of the words "compares less than 0".
"acompares less than zero" means that a < 0 is true.
"a compares == 0 means that a == 0 is true.
The other expressions I'm sure make sense now right?
Yes, an "object compares less than 0" means that object < 0 will yield true. Likewise, compares equal to 0 means object == 0 will yield true, and compares greater than 0 means object > 0 will yield true.
As to why he doesn't use the phrase "is less than 0", I'd guess it's to emphasize that this is all that's guaranteed. For example, this could be essentially any arbitrary type, including one that doesn't really represent an actual value, but instead only supports comparison with 0.
Just, for example, let's consider a type something like this:
class comparison_result {
enum { LT, GT, EQ } res;
friend template <class Integer>
bool operator<(comparison_result c, Integer) { return c.res == LT; }
friend template <class Integer>
bool operator<(Integer, comparison_result c) { return c.res == GT; }
// and similarly for `>` and `==`
};
[For the moment, let's assume the friend template<...> stuff is all legit--I think you get the basic idea, anyway).
This doesn't really represent a value at all. It just represents the result of "if compared to 0, should the result be less than, equal to, or greater than". As such, it's not that it is less than 0, only that it produces true or false when compared to 0 (but produces the same results when compared to another value).
As to whether <0 being true means that >0 and ==0 must be false (and vice versa): there is no such restriction on the return type for the operator itself. The language doesn't even include a way to specify or enforce such a requirement. There's nothing in the spec to prevent them from all returning true. Returning true for all the comparisons is possible and seems to be allowed, but it's probably pretty far-fetched.
Returning false for all of them is entirely reasonable though--just, for example, any and all comparisons with floating point NaNs should normally return false. NaN means "Not a Number", and something that's not a number isn't less than, equal to or greater than a number. The two are incomparable, so in every case, the answer is (quite rightly) false.
I think the other answers so far have answered mostly what the result of the operation is, and that should be clear by now. #VTT's answer explains it best, IMO.
However, so far none have answered the English language behind it.
"The object compares less than zero." is simply not standard English, at best it is jargon or slang. Which makes it all the more confusing for non-native speakers.
An equivalent would be:
A comparison of the object using <0 (less than zero) always returns true.
That's quite lengthy, so I can understand why a "shortcut" was created:
The object compares less than zero.
It means that the expression will return an object that can be compared to <0 or >0 or ==0.
If a and b are integers, then the expression evaluates to a negative value (probably -1) if a is less than b.
The expression evaluates to 0 if a==b
And the expression will evaluates to a positive value (probably 1) if a is greater than b.
What would be a quick and accurate way of initializing a complex array? Are there any differences in the following:
complex(real64) :: x(2,2)
x = 0
x = 0d0
x = (0d0,0d0)
Are they the same and can I assume I get the same result on every compiler? Does the standard say anything about it? The question is specifically about initializing to zero, not any other number.
Before coming to the real answer, a note about terminology. In Fortran what you have here is not initialization but assignment. These are two distinct things. Yes, it's clear what is meant here but it's perhaps good to become aware of the distinction.
The lines like x=0 are intrinsic assignment statements. To the left of = is the variable and to the right the expression. For each of these in the question there are two considerations:
the variable and expression may differ in type or type parameter;
the variable is an array and the expression a scalar.
As stated in a related answer, when the variable and expression differ in type or type parameter (but are type conformable as in this case) the expression is converted to the type and type parameter of the variable.
The first two assignments certainly involve type conversion: 0 is an integer and 0d0 is a (double precision) real. Although (0d0, 0d0) is a complex - so there is no type conversion - this will be of different kind unless double precision is the same as real(real64).
So, for at least the first two we have conversion like the equivalent
x = CMPLX(0, KIND=real64)
x = CMPLX(0d0, KIND=real64)
and for the third perhaps
x = CMPLX((0d0, 0d0), KIND=real64)
With
x = (0._real64, 0._real64)
we can be sure the kinds of the variable and the expression are the same.
As long as CMPLX(0, KIND=real64) has the same value as (0._real64, 0._real64) you can be sure that the assignment x=0 (for now, for scalar x) has the same effect as x=(0._real64, 0._real64). This is mandated by the Fortran standard.
Zero is somewhat of a special case in that it appears in every model number set, but whenever a (mathematical) number is used which can be exactly represented in all three the effect will be the same. Equally,
x = 3.14_real64
and
x = (3.14_real64, 0._real64)
are equivalent.
Perhaps worth noting, though, is that some compilers with some options may offer warnings about implicit conversion. That is one difference to be observed even with the numeric values being equivalent.
Coming to the array/scalar aspect: the expression is treated as though it is an array of the same shape as the variable with every element equal to that scalar value.
To conclude: each of those three assignments has the same Fortran-effect. After the assignment x is an array of given shape with each element having complex value (0._real64, 0._real64).
Now, that isn't to say that there won't be a possibility of something exciting happening at a low level, and that zero-setting may be a special case. But that's something between you and your compiler (and system).
I've looked at a bunch of questions regarding sequence points, and haven't been able to figure out if the order of evaluation for x*f(x) is guaranteed if f modifies x, and is this different for f(x)*x.
Consider this code:
#include <iostream>
int fx(int &x) {
x = x + 1;
return x;
}
int f1(int &x) {
return fx(x)*x; // Line A
}
int f2(int &x) {
return x*fx(x); // Line B
}
int main(void) {
int a = 6, b = 6;
std::cout << f1(a) << " " << f2(b) << std::endl;
}
This prints 49 42 on g++ 4.8.4 (Ubuntu 14.04).
I'm wondering whether this is guaranteed behavior or unspecified.
Specifically, in this program, fx gets called twice, with x=6 both times, and returns 7 both times. The difference is that Line A computes 7*7 (taking the value of x after fx returns) while Line B computes 6*7 (taking the value of x before fx returns).
Is this guaranteed behavior? If yes, what part of the standard specifies this?
Also: If I change all the functions to use int *x instead of int &x and make corresponding changes to places they're called from, I get C code which has the same issues. Is the answer any different for C?
In terms of evaluation sequence, it is easier to think of x*f(x) as if it was:
operator*(x, f(x));
so that there are no mathematical preconceptions on how multiplication is supposed to work.
As #dan04 helpfully pointed out, the standard says:
Section 1.9.15: “Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced.”
This means that the compiler is free to evaluate these arguments in any order, the sequence point being operator* call. The only guarantee is that before the operator* is called, both arguments have to be evaluated.
In your example, conceptually, you could be certain that at least one of the arguments will be 7, but you cannot be certain that both of them will. To me, this would be enough to label this behaviour as undefined; however, #user2079303 answer explains well why it is not technically the case.
Regardless of whether the behaviour is undefined or indeterminate, you cannot use such an expression in a well-behaved program.
The evaluation order of arguments is not specified by the standard, so the behaviour that you see is not guaranteed.
Since you mention sequence points, I'll consider the c++03 standard which uses that term while the later standards have changed wording and abandoned the term.
ISO/IEC 14882:2003(E) §5 /4:
Except where noted, the order of evaluation of operands of individual operators and subexpressions of individual expressions, and the order in which side effects take place, is unspecified...
There is also discussion on whether this is undefined behaviour or is the order merely unspecified. The rest of that paragraph sheds some light (or doubt) on that.
ISO/IEC 14882:2003(E) §5 /4:
... Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored. The requirements of this paragraph shall be met for each allowable ordering of the subexpressions of a full expression; otherwise the behavior is undefined.
x is indeed modified in f and it's value is read as an operand in the same expression where f is called. And it's not specified whether x reads the modified or non-modified value. That might scream Undefined Behaviour! to you, but hold your horses, because the standard also states:
ISO/IEC 14882:2003(E) §1.9 /17:
... When calling a function (whether or not the function is inline), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body. There is also a sequence point after the copying of a returned value and before the execution of any expressions outside the function 11) ...
So, if f(x) is evaluated first, then there is a sequence point after copying the returned value. So the above rule about UB does not apply because the read of x is not between the next and previous sequence point. The x operand will have the modified value.
If x is evaluated first, then there is a sequence point after evaluating the arguments of f(x) Again, the rule about UB does not apply. In this case x operand will have the non-modified value.
In summary, the order is unspecified but there is no undefined behaviour. It's a bug, but the outcome is predictable to some degree. The behaviour is the same in the later standards, even though the wording changed. I'll not delve into those since it's already covered well in other good answers.
Since you ask about similar situation in C
C89 (draft) 3.3/3:
Except as indicated by the syntax 27 or otherwise specified later (for the function-call operator () , && , || , ?: , and comma operators), the order of evaluation of subexpressions and the order in which side effects take place are both unspecified.
The function call exception is already mentioned here. Following is the paragraph that implies the undefined behaviour if there were no sequence points:
C89 (draft) 3.3/2:
Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored.26
And here are the sequence points defined:
C89 (draft) A.2
The following are the sequence points described in 2.1.2.3
The call to a function, after the arguments have been evaluated (3.3.2.2).
...
... the expression in a return statement (3.6.6.4).
The conclusions are the same as in C++.
A quick note on something I don't see covered explicitly by the other answers:
if the order of evaluation for x*f(x) is guaranteed if f modifies x, and is this different for f(x)*x.
Consider, as in Maksim's answer
operator*(x, f(x));
now there are only two ways of evaluating both arguments before the call as required:
auto lhs = x; // or auto rhs = f(x);
auto rhs = f(x); // or auto lhs = x;
return lhs * rhs
So, when you ask
I'm wondering whether this is guaranteed behavior or unspecified.
the standard doesn't specify which of those two behaviours the compiler must choose, but it does specify those are the only valid behaviours.
So, it's neither guaranteed nor entirely unspecified.
Oh, and:
I've looked at a bunch of questions regarding sequence points, and haven't been able to figure out if the order of evaluation ...
sequence points are a used in the C language standard's treatment of this, but not in the C++ standard.
In the expression x * y, the terms x and y are unsequenced. This is one of the three possible sequencing relations, which are:
A sequenced-before B: A must be evaluated, with all side-effects complete, before B begins evaluationg
A and B indeterminately-sequenced: one of the two following cases is true: A is sequenced-before B, or B is sequenced-before A. It is unspecified which of those two cases holds.
A and B unsequenced: There is no sequencing relation defined between A and B.
It is important to note that these are pair-wise relations. We cannot say "x is unsequenced". We can only say that two operations are unsequenced with respect to each other.
Also important is that these relations are transitive; and the latter two relations are symmetric.
unspecified is a technical term which means that the Standard specifies a set number of possible results. This is different to undefined behaviour which means that the Standard does not cover the behaviour at all. See here for further reading.
Moving onto the code x * f(x). This is identical to f(x) * x, because as discussed above, x and f(x) are unsequenced, with respect to each other, in both cases.
Now we come to the point where several people seem to be coming unstuck. Evaluating the expression f(x) is unsequenced with respect to x. However, it does not follow that any statements inside the function body of f are also unsequenced with respect to x. In fact, there are sequencing relations surrounding any function call, and those relations cannot be ignored.
Here is the text from C++14:
When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. [Note: Value computations and side effects associated with different argument expressions are unsequenced. —end note ]
Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with
respect to the execution of the called function.
with footnote:
In other words, function executions do not interleave with each other.
The bolded text clearly states that for the two expressions:
A: x = x + 1; inside f(x)
B: evaluating the first x in the expression x * f(x)
their relationship is: indeterminately sequenced.
The text regarding undefined behaviour and sequencing is:
If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, and they are not potentially concurrent (1.10), the behavior is undefined.
In this case, the relation is indeterminately sequenced, not unsequenced. So there is no undefined behaviour.
The result is instead unspecified according to whether x is sequenced before x = x + 1 or the other way around. So there are only two possible outcomes, 42 and 49.
In case anyone had qualms about the x in f(x), the following text applies:
When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function.
So the evaluation of that x is sequenced before x = x + 1. This is an example of an evlauation that falls under the case of "specifically sequenced before" in the bolded quote above.
Footnote: the behaviour was exactly the same in C++03, but the terminology was different. In C++03 we say that there is a sequence point upon entry and exit of every function call, therefore the write to x inside the function is separated from the read of x outside the function by at least one sequence point.
You need to distinguish:
a) Operator precedence and associativity, which controls the order in which the values of subexpressions are combined by their operators.
b) The sequence of subexpression evaluation. E.g. in the expression f(x)/g(x), the compiler can evaluate g(x) first and f(x) afterwards. Nonetheless, the resulting value must be computed by dividing respective sub-values in the right order, of course.
c) The sequence of side-effects of the subexpressions. Roughly speaking, for example, the compiler might, for sake of optimization, decide to write values to the affected variables only at the end of the expression or any other suitable place.
As a very rough approximation, you can say, that within a single expression, the order of evaluation (not associativity etc.) is more or less unspecified. If you need a specific order of evaluation, break down the expression into series of statements like this:
int a = f(x);
int b = g(x);
return a/b;
instead of
return f(x)/g(x);
For exact rules, see http://en.cppreference.com/w/cpp/language/eval_order
Order of evaluation of the operands of almost all C++ operators is
unspecified. The compiler can evaluate operands in any order, and may
choose another order when the same expression is evaluated again
As the order of evaluation is not always the same hence you may get unexpected results.
Order of evaluation
If I have the following code:
int a = 1;
bool b = 1;
Does a equal to b? Even the program might return that they are the same, are they actually equal in all aspects in low level?
Also, if I use code (pseudo) such as:
if (a)
then execute();
will execute() run? I am asking for theoretical answers, and I can't convince myself with experiments as this is not natural science. Thank you all.
I think you can convince yourself with the right experiments:
#include <type_traits>
int main() {
int a = 1;
bool b = 1;
static_assert(! std::is_same<decltype(a), decltype(b)>::value,
"No, they are not the same on all aspects");
}
Perhaps the most important difference between the two is that bool can only have two values: true and false, while int can have many more. Here's another experiment that shows a consequence of this:
#include <cassert>
int main() {
int a = 2;
bool b = 2;
assert(a != b);
}
The two types may seem similar because there are implicit conversions between the two. Any integral expression that is zero can be implicitly converted to false, and any integral expression that is not zero can be implicitly converted to true. In the opposite direction, false can be implicitly converted to zero, and true converted to one. This leads to the code above ending up testing if 2 != 1.
Now the answer to the question of whether execute(); is called in the snippet from the question should be obvious: the value a will be converted to a bool in the if statement, and since it is not zero, it will convert to true and result in a call to execute().
A non-zero numerical or pointer expression will evaluate to true when used in a boolean context. Similarly, a zero expression will evaluate to false. However, an int and a bool are different types, so they are not the same.
Does a equal to b? Even the program might return that they are the same, are they actually equal in all aspects in low level?
It all depends on what you mean by equal. The type is different, and that means that the representation in memory will probably differ (the compiler is free to represent those two as exactly the same, but it is also free to do otherwise). In most compilers/architectures, bool takes just one byte of storage and int has a bigger size (usually 4 bytes, but this depends on the architecture).
Besides the different sizes, the compiler will treat both types differently (not just the loads and stores to memory, but also the operations will be different. You can only store 0 and 1 in a bool, and that means that some operations might use that knowledge. For example, in this article you will find one case where the implementation of the test of a condition differs (note, the article has a case of undefined behavior that causes a bool to evaluate to both true and false as for the test the compiler is assuming that the bool can only be either 0 or 1, that cannot happen with int)
From a logic point of view, the language determines how the different types are used in operations, and in particular if you try to compare a and b in your program, the result of the expression will be true. Note that it does not mean that they are exactly the same, the language defines a set of conversion rules that are used to transform both variables into the same type and the comparison is performed in that type. In this case the conversion will be to int. The bool variable will be converted to 0 if false or to 1 if true.
Also, if I use code (pseudo) such as: if (a) execute(), will execute() run?
Yes. In this case, the condition inside the if requires a bool value, so the conversion will be from int to bool. The standard defines that conversion to yield false if the integer value is 0, or true otherwise, effectively doing the equivalent of if (a!=0). Since a is 1, the condition holds and execute() will be evaluated.
This thread talks about c++ booleans in comparison to chars, but it'll give you a good idea of what's going on under the hood. Why is a char and a bool the same size in c++?
Like the title states, are the integer (or any numerical datatypes like float etc.) comparison operators (==, !=, >, >=, <, <=) short circuited in C++?
They can't short circuit. To know if x == y, x != y, etc are true or false you need to evaluate both, x and y. Short circuiting refers to logical boolean operators && and ||. Logical AND is known to be false if the first argument is false and Logical OR is known to be true if the first argument is true. In these cases you don't need to evaluate the second argument, this is called short circuiting.
Edit: this follows the discussions for why x >= y don't short circuit when the operands are unsigned ints and x is zero:
For logical operands short circuiting comes for free and is implementation neutral. The machine code for if(f() && g()) stmt; is likely to look similar to this:
call f
test return value of f
jump to next on zero
call g
test return value of g
jump to next on zero
execute stmt
next: ...
To prevent short circuiting you actually need to do the computation of the result of the operator and test it after that. This takes you a register and makes the code less efficient.
For non-logical operators the situation is the opposite. Mandating short circuiting implies:
The compiler can't choose an evaluation of the expression that uses a minimum number of registers.
The semantics may be implementation defined (or even undefined) for many cases, like when comparing with maximum value.
The compiler needs to add an additional test/jump. For if(f() > g()) stmt; the machine code will look like this:
call f
mov return value of f to R1
test return value of f
jump to next on zero
call g
compare R1 with return value of g
jump to next on less-than equal
execute stmt
next: ...
Note how the first test and jump are just unnecessary otherwise.
No. The comparison operators require both operands to evaluate the correct answer. By contrast, the logical operators && and || in some cases don't need to evaluate the right operand to get the right answer, and therefore do "short-circuit".
No, how could they be. In order to check whether 1 == 2 you have to inspect both the 1 and the 2. (Ofcoruse, a compiler can do a lot of reordering, static checking, optimizations, etc. but that's not inherit to c++)
How would that work? Short-circuiting means you can avoid evaluating the RHS based solely on the result of evaluating the LHS.
e.g.
true || false
doesn't need to evaluate the RHS because true || x is true no matter what x turns out to be.
But this won't work for any of the comparisons that you list. For example:
5 == x
How can you ever know the result of the expression without knowing x?