https://en.cppreference.com/w/cpp/algorithm/reduce
It says that the behavior of an operation is not defined if the operation is not commutative, but why? We just divide the array into blocks and then merge the result. Is it only necessary to have associativity?
std::reduce requires both associativity and commutativity. Associativity is clearly needed for a parallel algorithm, since you want to perform the calculation on separate chunks and then combine them.
As for commutativity: According to a reddit post by MSVC STL developer Billy O'Neal, this is required in order to allow vectorization to SIMD instructions:
Commutativity is also necessary to enable vectorization, since the code you want for reduce to come out as something like:
vecRegister = load_contiguous(first);
while (a vector register sized chunk is left) {
first += packSize;
vecRegister = add_packed(load_contiguous(first), vecRegister);
}
// combine vecRegister's packed components
etc., which given ints and SSE registers and a * b * c * d * e * f * g * h gives something like (a * e) * (b * f) * (c * g) * (d * h).
Most other languages aren't doing explicit things to make vectorizing their reduction possible. And nothing says we can't add a noncommutative_reduce or something like that in the future if someone comes up with a compelling use case.
The behavior is actually non-deterministic if the operation between the operands is not commutative. "non-deterministic" is not the same as "undefined". Floating point math is not commutative, for example. This is one reason why a call to std::reduce may not be deterministic, because the binary function is applied in an unspecified order.
Refer to this note in the standard:
Note: The difference between reduce and accumulate is that reduce applies binary_op in an
unspecified order, which yields a nondeterministic result for non-associative or non-commutative
binary_op such as floating-point addition. —end note ]
The standard defines the generalized sum as follows: numeric.defns
Define GENERALIZED_NONCOMMUTATIVE_SUM(op, a1, ..., aN) as follows:
a1 when N is 1, otherwise
op(GENERALIZED_NONCOMMUTATIVE_SUM(op, a1, ..., aK),
op(GENERALIZED_NONCOMMUTATIVE_SUM(op, aM, ..., aN)) for any K where 1
Define GENERALIZED_SUM(op, a1, ..., aN) as GENERALIZED_NONCOMMUTATIVE_SUM(op, b1, ..., bN), where b1, ..., bN may be any permutation of a1, ..., aN.
So, the order of summation as well as the order of operands is unspecified. So if the binary operation is not commutative or not associative, the result is unspecified.
That is also explicitly stated here.
Regarding why: It gives the library vendors more freedom, so they may or may not implement it better. As an example where the implementation can benefit from commutativity. Consider the sum a+b+c+d+e, we first calculate a+b and c+d in parallel. Now a+b returns before c+d does (as it can happen, because it is done in parallel). Instead of waiting for the return value of c+d we now can directly compute (a+b)+e and then add this result to the result of c+d. So in the end, we computed ((a+b)+e)+(c+d), which is a rearrangement of a+b+c+d+e.
Why does std::reduce need commutativity?
For speed.
If the operator is commutative, then you can rearrange the order of operations without affecting the results.
And if you can rearrange the order of operations, you can have different threads, or processes, or hardware accelerators or what-not working independently on some of the operations to be performed, and not care about the order in which they complete their partial sums, nor their internal ordering of operations, and then finally add up the partial sums whichever way is convenient.
Related
I've into curious question (asking it myself while reading a crude piece of code). Let's look at expression:
double a = c*d*e*2/3*f;
where c, d, e, f are initialized variables of type double. Does standard guarantee that it would be treated as c*d*e*2 (double result) then divided by 3 and multiplied by f (or some similar behavior). Obviously, 2/3 being calculated to 0 is undesirable.
Which paragraph of standard defines that?
Based on the standard
[intro.abstract] - Note 7 (non-normative):
Operators can be regrouped according to the usual mathematical rules
only where the operators really are associative or commutative.
Mathematical rule for MDAS is from left to right (considering the associativity and precedence of operators). So it is evaluated as follows:
(((((c * d) * e) * 2) / 3) * f)
In a word - yes.
The property you're looking for is called operator associativity. It defines how operators of the same precedence (such as * and /) are grouped and ordered when parenthesis aren't present.
In your case, both * and / have the same precedence, and are both left-associative - i.e., they are evaluated from left to right. Which means c would be multiplied by d, then the result by e, then the result by 2 (which would be done with floating point arithmetic, since you're multiplying a double by an int literal), then divided by 3 (again, using floating point arithmetic) and finally multiplied by f.
See this cppreference page for additional information.
Both * and / have the same precedence, and are left-to-right associative, this means that
a*b*c*d
is parsed as
((a*b)*c)*d
and the same is true if you replace any of the * with /.
Source: http://en.cppreference.com/w/cpp/language/operator_precedence
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.
I am Trying to write a Matrix calculator, and have come across a difficulty. If I have A = B*C, then B*C is evaluated with my overloaded operator*(const Matrix&), in which I need some Temporary object for my calculations. This works fine. But once I come to more complicated stuff, I need more temporary objects, and obviously I don't want to do that by hand/have a miximum.
Say i want to evaluate:
Matrix A = B*C + D*E*F + G*H;
Then operator precedence tells me, that the * will be evaluated first. So:
Step 1) B*C -> t1
Step 2) D*E -> t2
Step 3) t2*F-> t3
Step 4) G*H -> t4
Step 5) t1+t2 -> t5
Step 6) t5+t4 -> t6
Step 7) A=t6
Is this the exact order? Or is the order non deterministic? My idea was to create A linked list, and i will check in my + and *, if the objects that are being calculated (eg. t2*F) are temporary, then I can remove them/insert appropriately.
Expression templates can solve this problem. For example
Matrix M = 2*(A+B) + C;
Would normally create 3 temporary objects for A+B, 2*(A+B), 2*(A+B) + C. So this could get inefficient when Matrix objects are heavyweight.
Expression templates are a template metaprogramming technique where the operators are instead implemented such that they return an object that represents the entire expression, but compute the expression only at the end.
So 2*(A+B) + C would yield an object of a type something like
sum<multiple<float, sum<Matrix, Matrix>>, Matrix>
Which is capable of calculating M without allocating intermediary matrices. (in this case by component-wise addition and multiplication by a constant). That object itself is generated at zero runtime overhead.
Because this is complex, it may be better to use a linear algebra library for this, for example Eigen.
Operator precedence does not tell you which order things will be evaluated in. It tells you what the operands are for each operator.
The only guarantee of order of evaluation is that an operator's operands must be evaluated before that operator can operate.
To look at a simpler example, 2 + 3 * 4, precedence says that the thing being added to 2 is 3 * 4, as opposed to 3.
In your example B*C + D*E*F + G*H the only guarantees are:
G*H is evaluated before the second +
B*C is evaluated before the first +
D*E*F is evaluated before either +
The first + is evaluated before the second +
Within D*E*F, the first * is evaluated before the second *.
A possible ordering might be:
(1) D*E
(2) G*H
(3) B*C
(4) 1*F
(5) 3+4
(6) 5+2
Also note that if B, C etc. require any evaluation themselves , then those evaluations don't have any relative ordering either.
my idea was to create A linked list, and i will check in my + and *,
if the objects that are being calculated (eg. t2*F) are temporary,
then I can remove them/insert appropriatly.
Don´t.
This makes absolutely no sense, only additional problems. Big times.
The compiler can handle everything for you, you can safely rely on that. C++ is not Assembler-level. Just return temporary stack-based objects (no pointer or anything). The intermediate results of your calculation will clean up themselves.
Matrix operator+(const Matrix&) {...}
Matrix operator*(const Matrix&) {...}
and each such function has one temporary object insde to calcualte, which is returned then.
That´s everything you need.
I've just read that order of evaluation and precedence of operators are different but related concepts in C++. But I'm still unclear how those are different but related?.
int x = c + a * b; // 31
int y = (c + a) * b; // 36
What does the above statements has to with order of evaluation. e.g. when I say (c + a) am I changing the order of evaluation of expression by changing its precedence?
The important part about order of evaluation is whether any of the components have side effects.
Suppose you have this:
int i = c() + a() * b();
Where a and b have side effects:
int global = 1;
int a() {
return global++;
}
int b() {
return ++global;
}
int c() {
return global * 2;
}
The compiler can choose what order to call a(), b() and c() and then insert the results into the expression. At that point, precedence takes over and decides what order to apply the + and * operators.
In this example the most likely outcomes are either
The compiler will evaluate c() first, followed by a() and then b(), resulting in i = 2 + 1 * 3 = 5
The compiler will evaluate b() first, followed by a() and then c(), resulting in i = 6 + 2 * 2 = 10
But the compiler is free to choose whatever order it wants.
The short story is that precedence tells you the order in which operators are applied to arguments (* before +), whereas order of evaluation tells you in what order the arguments are resolved (a(), b(), c()). This is why they are "different but related".
"Order of evaluation" refers to when different subexpressions within the same expression are evaulated relative to each other.
For example in
3 * f(x) + 2 * g(x, y)
you have the usual precedence rules between multiplication and addition. But we have an order of evaluation question: will the first multiplication happen before the second or the second before the first? It matters because if f() has a side effect that changes y, the result of the whole expression will be different depending on the order of operations.
In your specific example, this order of evaluation scenario (in which the resulting value depends on order) does not arise.
As long as we are talking about built-in operators: no, you are not changing the order of evaluation by using the (). You have no control over the order of evaluation. In fact, there's no "order of evaluation" here at all.
The compiler is allowed to evaluate this expression in any way it desires, as long as the result is correct. It is not even required to use addition and multiplication operations to evaluate these expressions. The addition and multiplication only exist in the text of your program. The compiler is free to totally and completely ignore these specific operations. On some hardware platform, such expressions might be evaluated by a single atomic machine operation. For this reason, the notion of "order of evaluation" does not make any sense here. There's nothing there that you can apply the concept of "order" to.
The only thing you are changing by using () is the mathematical meaning of the expression. Let's say a, b and c are all 2. The a + b * c must evaluate to 6, while (a + b) * c must evaluate to to 8. That's it. This is the only thing that is guaranteed to you: that the results will be correct. How these results are obtained is totally unknown. The compiler might use absolutely anything, any method and any "order of evaluation" as long as the results are correct.
For another example, if you have two such expressions in your program following each other
int x = c + a * b;
int y = (c + a) * b;
the compiler is free to evaluate them as
int x = c + a * b;
int y = c * b + x - c;
which will also produce the correct result (assuming no overflow-related problems). In which case the actual evaluation schedule will not even remotely look like something that you wrote in your source code.
To put it short, to assume that the actual evaluation will have any significant resemblance to what you wrote in the source code of your program is naive at best. Despite popular belief, built-in operators are not generally translated in their machine "counterparts".
The above applies to built-in operators, again. Once we start dealing with overloaded operators, things change drastically. Overloaded operators are indeed evaluated in full accordance with the semantic structure of the expression. There's some freedom there even with overloaded operators, but it is not as unrestricted as in case of built-in operators.
The answer is may or may not.
The evaluation order of a, b and c depends on the compiler's interpretation of this formula.
Consider the below example:
#include <limits.h>
#include <stdio.h>
int main(void)
{
double a = 1 + UINT_MAX + 1.0;
double b = 1 + 1.0 + UINT_MAX;
printf("a=%g\n", a);
printf("b=%g\n", b);
return 0;
}
Here in terms of math as we know it, a and b are to be computed equally and must have the same result. But is that true in the C(++) world? See the program's output.
I want to introduce a link worth reading with regard to this question.
The Rules 3 and 4 mention about sequence point, another concept worth remembering.
When a expression is evaluated in C/C++, does it follow BODMAS [Bracket open Division Multiply Addition Substraction] rule? If not then how they are evaluated?
EDIT: More clearly, If the following expression is evaluated according to BODMAS rule,
(5 + 3)/8*9
First what is in brackets is processed.
8/8*9.
Then Division is done.
1*9
And then multiplication and so on.
There are far more operators than that. You can find a precedence tables for C++ and C.
But yes, you'll find it respects that. (Though I'm not sure it's exactly what you've said...)
There are two answers to this question.
One is that C++ does follow standard mathematical precedence rules, which you refer to as BODMAS. You can see the order in which C++ associates all its operators here.
However, if any of the expressions involved in the operation have side effects, then C++ is not guaranteed to evaluate them in what one might consider to be standard mathematical order. That's sort of an advanced topic, however.
Other people have given you links to operator precedence lists. These are well and good. However, if you need to look at an operator precedence table to determine what your code tells computers to do, please take pity on your code's maintainers (including future you) and just use parentheses. It makes your intention much clearer and saves time and heartache in the long run.
C++: http://msdn.microsoft.com/en-us/library/126fe14k.aspx
C#: http://msdn.microsoft.com/en-us/library/aa691323(VS.71).aspx
C applies the operators in arithmetic expressions in a precise sequence determined by the following rules of operator precedence, which are generally the same as those in algebra:
Operators in expressions contained within pairs of parentheses are evaluated first. Parentheses are said to be at the “highest level of precedence.” In cases of nested, or embedded, parentheses, such as
( ( a + b ) + c )
the operators in the innermost pair of parentheses are applied first.
Multiplication, division and remainder operations are applied next. If an expression contains several multiplication, division and remainder operations, evaluation proceeds from left to right. Multiplication, division and remainder are said to be on the same level of precedence.
Addition and subtraction operations are evaluated next. If an expression contains several addition and subtraction operations, evaluation proceeds from left to right. Addition and subtraction also have the same level of precedence, which is lower than the precedence of the multiplication, division and remainder operations.
The assignment operator (=) is evaluated last.
I found an expression that doesn’t follow “BODMAS”. Here is my c program for your reference
#include <stdio.h>
int main() {
int a = 6;
int b = 4;
int c = 2;
int result;
result = a - b + c; // 4
printf("%d \n", result);
result = a + b / c; // 8
printf("%d \n", result);
result = (a + b) / c; // 5
printf("%d \n", result);
return 0;
}