0

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?

Swift - Friday Pie
  • 12,777
  • 2
  • 19
  • 42
  • Possible duplicate of [C/C++ Math Order of Operation](https://stackoverflow.com/q/11296854/608639), [What are the rules governing C++ single and double precision mixed calculations?](https://stackoverflow.com/q/4239770/608639), [Order of operations to maximize precision](https://stackoverflow.com/q/45524072/608639), etc. – jww Mar 27 '18 at 07:43
  • @jww narrowed down the question, the purpose was the actual clause in standard that would apply here – Swift - Friday Pie Mar 27 '18 at 07:45
  • 3
    As previously noted by YSC, mathematical terminology is used very loosely or even incorrectly here. Commutativity means reordering *operands*, not operations, e.g. `a*b` vs `b*a`. What you mean is associativity, e.g. `(a*b)*c` vs `a*(b*c)`. Multiplication is mathematically both associative and commutative, but FP multiplication is not associative because of potentially different rounding of intermediate results. Division however is neither associative nor commutative. `a*b/c` is a special case where division is math. associative with multiplication. – Arne Vogel Mar 27 '18 at 08:15

3 Answers3

2

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)
Joseph D.
  • 11,804
  • 3
  • 34
  • 67
  • The section you linked to is about function objects. That quote isn't anywhere near there. – StoryTeller - Unslander Monica Mar 27 '18 at 07:35
  • 1
    I knew it should have been there! your link points to wrong paragraph though, it's in p.4.1.1. But knowing the wording, I've found it. – Swift - Friday Pie Mar 27 '18 at 07:37
  • 4
    Now it's section 1.7 which just doesn't exist. Just place a the section **name** into your answer, i.e. `[chpater.subchapter.etc]` with a paragraph number. That won't shift too much among revisions and will future proof your answer. It's also more informative than a blanket "the standard". – StoryTeller - Unslander Monica Mar 27 '18 at 07:39
  • 4
    This is `[intro.abstract]/7`, but it is a non normative note (and you _should_ mention it). A better quote should exist. – YSC Mar 27 '18 at 07:45
  • @YSC I suppose , I should be paranoid and recommend use `(2.0/3.0)*c*d*e*f` anyway – Swift - Friday Pie Mar 27 '18 at 07:54
  • @Swift no you should not, but you should also write code with the first objective to maximise readibility. So yes, write something like `double a = ((c*d*e*2)/3)*f;` so even a newbie could understand what's going on. Beware though, float multiplication is not associative (`(a*b)*c != a*(b*c)`). – YSC Mar 27 '18 at 07:57
  • @YSC it's not matter of readability, it's matter of correct execution on set of quite exotic platforms with non-mainstream compilers (e.g. it is must c++1x, but it doesn't have `constexpr`), with harsh limitation that it is impossible to test on target platforms (they don't exist yet an are not communicable). other values are not ordered currently in any regard to expected values anyway while order of magnitude actually was known (it's part of noise filter matrix) – Swift - Friday Pie Mar 27 '18 at 08:02
  • @Swift its always a matter a readability. If you neglect it, you'll get bugs, hard-to-fix bugs. – YSC Mar 27 '18 at 08:05
  • @YSC then you would be horrified by most of code, it's real-time application with no allocations at all and a lot done in global variables. And only way to see bug happening is either to emulate it theoretically (with no automated models of hardware present) or launch and see multi-million things explode [X)]. the code was just result of expansion of matrix into set of linear expression. I already had found few signed/unsigned comparisons mismatch and error in converting from geocentrical to NED. – Swift - Friday Pie Mar 27 '18 at 08:08
  • @Swift I've seen code like this. It's not because it exists that it should. – YSC Mar 27 '18 at 08:09
  • @YSC it's banned to write otherwise. essentially a "new" operator or anything that uses it is banned. That's real-time applications.. – Swift - Friday Pie Mar 27 '18 at 08:11
  • 1
    @Swift Where does that `new` come from? I was speaking about readability, not dynamic allocation. I've worked on embedded systems, it's not because you have specifics that you cannot write code easy-to-read. – YSC Mar 27 '18 at 08:14
  • @YSC in matter readability as matching it to canonical form 2.0/3.0 separately would be _more_ readable because 2/3 is present in formula of filter matrix at beginning. a*b*c*d in front appeared for reason known to author only (who is not here anymore). also that function got ten pages of documentation alone – Swift - Friday Pie Mar 27 '18 at 08:17
2

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.

Mureinik
  • 297,002
  • 52
  • 306
  • 350
1

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

Mor A.
  • 3,805
  • 2
  • 16
  • 19
  • 2
    @Swift floating point operations are not commutative, because of rounding errors on the intermediate results. If you use gcc you can use `--ffast-math` to ignore this. – mch Mar 27 '18 at 07:51
  • 4
    @mch I think you meant _associative_ ;) Sure `a*b == b*a` but `(a*b)*c != a*(b*c)`. – YSC Mar 27 '18 at 07:59
  • @mch I stand corrected! And no, not gcc, not even IEEE-754 binary. decimal version, 192 bit. – Swift - Friday Pie Mar 27 '18 at 08:21