Power operator (^) precedence levels and evaluation order
28 次查看(过去 30 天)
显示 更早的评论
creepydog
2024-6-21
While
2^-1^-2
returns 4, which indicates a "left to right" evaluation order (as it is common in many programming languages),
2^-1^-2^3
returns 0.125, which I find surprising – I would have expected a value of 4^3 = 64.
To examine the order applied by Matlab, I tried all possible combinations:
((2^-1)^-2)^3 % returns 64 (1st, then 2nd operator)
(2^-1)^-(2^3) % returns 256 (1st and 3rd)
(2^-(1^-2))^3 % returns 0.125 (2nd, then 1st)
2^-((1^-2)^3) % returns 0.5 (2nd, then 3rd)
2^-(1^-(2^3)) % returns 0.5 (3rd, then 2nd)
So it seems that Matlab first evaluates the 2nd operator, then the 1st (=left), then the 3rd (=rightmost). That seems very odd. Can anybody explain why Matlab chooses this evaluation order?
I'm aware of https://de.mathworks.com/help/matlab/matlab_prog/operator-precedence.html, but it just doesn't make sense to me. According to the rules given there, the 3rd operator (^) in my example should even come first, as it has a higher precedence than the other two (^-) operators. So doesn't Matlab adhere to its own rules?
18 个评论
Paul
2024-6-21
The linked doc page states: "the operators (^-), (.^-), (^+), (.^+), (^~), and (.^~) work from second from the right to left"
Though unstated (it shouldn't be), presumably the rightmost of those operators is applied last.
In the first example, the "second from the right" is the leftmost operator which is applied first, and then the evaluation finishes with the rightmost operator. So it does go from left to right in that example because there are only two operators.
In the second example, the "second from the right" is the middle operator, then the evaluation works to the left to pick up the leftmost operator, and then the evaluation finishes with the rightmost operator.
So it does look like both examples adhere to the documentation.
Here's another example.
2^-1^-2^3^-4
(2^-(1^-(2^3)))^-4
I vaguely recall another thread on this forum that was a rather long discussion about a similar, if not the exact same, question.
creepydog
2024-6-21
Hi Paul. :-)
I disagree with your conclusion that my second example adheres to the rules. Please note that the operator precedence documentation defines two different levels of precedence for power operators: ^ is in level 2, while ^- is only in level 3. Left/right rules are only relevant to decide the order between operators within the same level. So in my second example (2^-1^-2^3) the rightmost operator ^ should come first according to the documentation due to its higher level.
If the rules were applied correctly, your example would be evaluated as ((2^-1)^-(2^3))^-4 because 2^3 has priority over the ^- operators.
Torsten
2024-6-21
编辑:Torsten
2024-6-21
When using such complicated expressions, I'd make my life easy and safe and place brackets where they need to be placed in order to reflect the expression I want to set. Why spending hours studying MATLAB documentation if a solution with brackets is so simple ?
And I always put negative expressions in brackets although this might not be necessary. Thus 2^(-1) instead of 2^-1.
creepydog
2024-6-21
@Paul Thank you for the interesting link. Yes, that thread touches most of the aspects of my question: the strange order (2nd, 1st, 3rd) and that the problem does not occur with only 2 operands. It does not mix ^ and ^-, though, but it mentions that fact somewhere at least. And it does not really answer the question. Now I'm pessimistic about getting a satisfying answer.
creepydog
2024-6-21
@Torsten I also recommend using parentheses where they improve readability or avoid misconception. But I don't want to spam unnecessary parentheses everywhere (and waste hours typing and reading them until my retirement). That's why there are precedence rules. Now that I know that they are broken in case of combinations of more than 1 (or 2?) power operators, I'll be more generous at typing parentheses if I ever come to need such an expression. I came up with this question when reading the precedence list.
dpb
2024-6-21
This (and the other thread) poiint out a problem with proprietary languages such as MATLAB -- it is not required there be a published language specification that unambiguously defines all corners of the language and has an accessible standards group to which one can submit such arcane cases to for resolution -- either explain the rule or cause a correction to be generated in the rare case that an actual problem is uncovered.
MATLAB, otoh, while there undoubtedly is somewhere within TMW (The Mathworks) something that purports to be/serves as such a standard, it is not available outside Natick and all the language definition provided is in the documentation which is, for the most part, example-based rather than be definitive rules.
Hence, there is no way to answer the question raised here with what is publicly available, TMW simply doesn't publish those considered proprietary details for competitive reasons.
One could submit a bug report and see what the response is, but "WAD" (Working as Designed) is probably about all that will be allowed to. Although it could be interesting to see how they might explain the way in which the calculation and the apparent application of the precedence rules differ -- or how the interpretation of the documentation on the rules can be interpreted to come up with the result.
Torsten
2024-6-21
编辑:Torsten
2024-6-21
But I don't want to spam unnecessary parentheses everywhere (and waste hours typing and reading them until my retirement).
I also don't like parentheses if they prevent readability of the code. But they are not unnecessary in case of complicated arithmetic structures because they immediately show how they are to be interpreted. You will waste time if you don't structure ambiguous expressions using parentheses because if you open your code after some time, you will have forgotten the software-specific precedence rules and have to consult the MATLAB documentation again (or make test runs as you did if the documentation does not answer what you are looking for).
Paul
2024-6-21
Surely operator precedence wouldn't be considered a corner of the language. If anything, it's one of the cornerstones of the language.
Even if MathWorks has an an internal, unpublished standards document, the fact is that the operator precedence rules are published and certainly seem to be written with the intent of being definitive (as they should be). Given that's the case, I think it reasonable to expect the documentation be consistent with actual operation (always).
dpb
2024-6-21
编辑:dpb
2024-6-21
@Paul -- the cases of mixing the two different precedence levels(*) I would submit is a corner -- how sharp of an angle is indeterminate as is the interpretation of the rules as written.
My conjecture is that the internal design document more than likely is definitive but the published description is not an exact translation but an attempt to narratize it into something less complicated. These thoughts are, of course, simply conjecture although the fact that there is no published standard as say, Fortran, C, ..., is fact; all we have is the documentation, not a definition of the language itself.
But, agreed, it would be desireable that the doc were accurate and complete, but the help-writing staff are always bound to leave stuff out and occasionally that may be critical when get to complex issues such as these "made-for-breaking" expressions.
(*) Is there another common language that has such a concept?
Paul
2024-6-22
Is there another common language that has such a concept as having expressions with operators of two (or more) different precedence levels? I know that you know that is the case in common languages, such as FORTRAN, so I'm sure I'm not understanding the concept to which you are refering.
For Matlab, I don't think there is any reason to distinguish between a standard and the documentation. MathWorks is a closed shop wrt to Matlab, so there shouldn't be any reason to be concerned about whether or not Matlab is compliant with some WG5-like standard, or implements extensions to the standard, etc. like one might be concerned with, say, Digital Visual Fortran. Because MathWorks controls it all, as a user I'm willing to accept that the doc is the de facto standard, even if, as you rightly pointed out, it's not really written like a standard.
Stephen23
2024-6-22
"Is there another common language that has such a concept?"
All languages must define this (even if their definition is simply "in the order that they occur"):
The ambiguity of human mathematical operators is the reason why improved notations, e.g. reverse polish, have been invented:
dpb
2024-6-22
@Paul said "For Matlab, I don't think there is any reason to distinguish between a standard and the documentation."
I think this thread disproves that -- and there are many places where the documentation is not complete in its descriptions of what happens although those typically are in higher-level functions rather than the base language.
But, agreed, there is nothing else for the user to go by so when it is either incomplete or wrong (or there is actually a bug in the language), the user has no recourse but to file a bug/support request; there is no reverting back to the Standard to try to make an interpretation oneself as one can at least attempt to do with standardized languages.
And, my bad on the other question raised; I should have reviewed the Fortran standard before writing that, sorry...
Paul
2024-6-22
If the documentation is not complete or is an error, then, ideally, the documentation needs to be fixed. If there is a bug in the Matlab implementation, then, ideally, that bug should be fixed. I think we agree on this.
For me, all this thread proves is that we have one of those two situations. FWIW, which is probably not much, when I need to deal with other programing languages (e.g., FORTRAN, C++) and need to understand how some code works, I just go to the vendor-provided user's guide, manual, or whatever they call it. I don't think I've ever gone to look up anything in a formal standard. Of course, YMMV.
Based on the historical discussion of this issue, I too have a conjecture as to why Matlab does this (peculiar?) start-from-second-from-the-right thing for bunch of operators, but it's just conjecture.
What I am mildly curious about is whether any other language defines operators analogous to Matlab's ^+ and ^- (and the others at the same precedence as those). I've never seen such, but I'm not knowledgeable about many languages. For example, in FORTRAN
3**-2
is an expression with two operators: exponentiation(**) and unary minus (-), whereas in Matab that expression would use a single operator (^-) .
If you are going to (or already have) opened a support request on this issue, if you don't mind please comment back on this thread with a summary MathWorks's adjudication.
dpb
2024-6-22
编辑:dpb
2024-6-24
If the documentation is not complete or is an error, then, ideally, the documentation needs to be fixed. If there is a bug in the Matlab implementation, then, ideally, that bug should be fixed. I think we agree on this."
If it is the former, I agree; I have submitted bug/enhancement reports for several corrections/additions to the documentation over the years; most if not all of those have been incorporated.
In this case, if there is actually a discrepancy between what the formal language specification requires(*) and the implementation (and not, as I suspect, simply that the user documentation is not complete in covering all possible combinations of operators and order of each), I would expect it will be the implementation that wins and the specification modified to match--there's simply too much precedence and historical code (including MATLAB/Mathworks code itself) to go munging on this.
(*) I continue in my belief there simply has to be such internally
"If you are going to (or already have) opened a support request on this issue, ..."
I think it is a case where a support request should indeed be submitted asking for clarification of the rules and that the documentation be amplified to be complete and unambiguous. I do not disagree with the general precept that lacking the formal design document to refer to, the user documentation should serve in its stead. The problem TMW has is that they chose to make the documentation example and narrative based(+) in order to try to be "user friendly" and consequently normative text gets the short end of the stick when things get sticky as here.
(+) It's annoying the Examples section comes before the normative definition of arguments although sometimes it's only by a combination of studying both one can finally understand some of the more complex syntax.
creepydog
2024-6-28
TMW has answered my support request (see accepted answer), which referred to this thread but contained its own example and focus. The answer isn't 42, but the corresponding question shouldn't be unknown anyway. So, for completeness and transparency, I'd like to post the full text of my support request here:
<quote>
If you run this expression in the Command Window:
>> 2^+3^2
Matlab returns ans = 64, which shows that it was evaluated from left to right, i.e. as (2^+3)^2. Otherwise the result would be 512.
The operator precedence documentation [1] says that there are two different precedence levels for power operators (levels 2 and 3). The normal matrix power operator ^ has precedence level 2, while the "matrix power with unary plus" operator ^+ has level 3, which is inferior to level 2. So, according to the documentation, 2^+3^2 should be evaluated as 2^+(3^2), because the ^ operator has higher precedence than ^+ (like 2+3*4 is 14, not 20).
There's a note in the documentation [1] saying that level 3 operators deviate from the normal left-to-right rule [2], but that only matters within (!) this precedence level 3. Operators of precedence level <= 2 still have priority over operators of level 3, so ^ must precede ^+ according to the documentation, regardless of left/right aspects.
Please also see my question and the discussion with/of several MVPs in Matlab Central [3] for more examples and thoughts about this problem.
[2] They "work from second from the right to left", I'm not sure what that means, linguistically.
</quote>
采纳的回答
Oliver Jaehrig
2024-6-28
The statement below is coming from our development team and they reviewed the discussion on MATLAB Answers and the incoming support request regarding it:
We agree that the current behavior is unusual. Unfortunately, it's decades-old MATLAB behavior. We may choose to change it, but it cannot change suddenly without adequate notice.
We strongly recommend using parentheses to clarify the intended order of operations. The Code Analyzer suggests this, and early versions of MATLAB actually required this.
To clarify what is meant by "second from the right to left" in the doc,
a^-b^-c^-d^-e^-f
is parsed as
(a ^ (- (b ^ (- (c ^ (- (d ^ (- e)))))))) ^ (- f)
which is essentially equivalent to
X1 = -(d^-e);
X2 = -(c^X1);
X3 = -(b^X2);
X4 = a^X3;
X5 = X4^-f
Our development team considers to enhance the documentation about this in a future release. If there are any further questions, please let me know, or create a new support request if needed.
18 个评论
creepydog
2024-6-28
Note: My first example "2^-1^-2" seemed to be evaluated "from left to right" in contrast to my second example "2^-1^-2^3" but in fact both of them are consistently evaluated according to the "second from the right to left[, finally the rightmost]" rule explained by the TMW's answer.
dpb
2024-6-28
Interesting. Did you check out how it works with the mixed levels of precedence which your original example used?
But, TMW response echoes my thinking that whatever the warts, the likelihood of changing anything in the implementation is near nil; it's just buried too deep into existing code.
creepydog
2024-6-28
编辑:creepydog
2024-6-28
Thanks for the hint. No, I didn't. The examples discussed here so far certainly don't cover all cases/combinations of mixed levels. TMW's answer made it clear what "second from the right to left" means (that might have been clear to others but it hasn't been to me) and that the documentation does not completely correctly describe Matlab's behavior at the moment because that order rule should only be applied for pure level 3 statements (i.e. after evaluating all level 2 operators) which it isn't. This can be seen from my second example "2^-1^-2^3".
My experiments suggest that this violation only occurs for the rightmost operator (but remember: proof by example is dangerous). One last example for this: While a^b^c^d^e^f is evaluated left-to-right, mixing in one level 3 operator at the beginning (a^+b^c^d^e^f) changes it to (a^(((b^c)^d)^e))^f. Note that it's still left-to-right from b to e, and that a^+ comes only after that, but the ^f is applied last despite being level 2.
Edit/Addendum: I now used the Symbolic Toolbox for my last example. I hope that doesn't introduce additional differences.
Paul
2024-6-29
I too was immediately struck by the fact that the Tech Support response did not address the issue in handling mixed levels of precedence, which you've again shown is documented incorrectly with your new example.
For me, their response about "is parsed as" raises a new question, namely that ^- (and others) might not really be a single operator as the doc implies. If it were, it couldn't be split by a (. Maybe they meant that what they showed would be the same computation effectively, even if that's not how it's actaully implemented. But they did say "is parsed as" not "is parsed in a way that's equivalent to." Maybe I'm reading too much into it.
dpb
2024-6-29
编辑:dpb
2024-6-29
The "-" in the example expression isn't part of a unary operator "^-" but the minus sign applied to the exponent. There is no unary operator of that form. Only ".^" and ".*" and the two forms of divide; minus and plus aren't included in the "dot" operator family.
To me, their response also clearly illustrates why the documentation isn't precisely the same thing as a language definition :J
That they chose to not respond to the truly interesting part may also be telling; maybe the definition isn't complete and behavior is undefined except by the implementation (and they're not sure themselves without more digging than taken time to have done?). Presuming @creepydog got a response from support rather than only the above post, I'd suggest sending back one or two of the mixed-level examples that isn't adressed.
Paul
2024-6-29
Hi dpb,
"The "-" in the example expression isn't part of a unary operator "^-" but the minus sign applied to the exponent. "
If "^-" is an operator at all, it wouldn't be a unary operator. It would be a binary operator.
But, as we've both pointed out, it looks like the "-" in "^-" is being treated as a unary operator uminus, - on the exponent, which is then followed by applying the binary operator "^" mpower, ^. In other words, "^-" appears to not be an operator and of itself.
" ... the operators (^-), (.^-), (^+), (.^+), ..."
which can't be ready any other way than that all four of those are each single (not unary) operators.
However, none of (^-), (.^-), (^+), (.^+) are actually listed as arithmetic operators at Matlab Operators and Special Characters, which strongly suggests that that those four (and the others with power and negation that we haven't been talking about) are actually treated as two, separate operations, as we both suspect. But if that's the case, I can't fathom why the Operator Precedence page is written the way it is.
dpb
2024-6-29
编辑:dpb
2024-6-29
For (^-) to be interpreted as a unary operator for exponentiation with a negative exponent the original expression would had to have been written as
2^--2
ans = 4
to be equivalent to
(1/2)^-2
ans = 4
so that part seems ok to me; the part that may be omitted in describing the parsing with negative exponents is that the sign character (if present) of the exponent is applied first because it's part of parsing a constant before it gets to the exponentiation part of the interpretation of the whole string. (Then again, maybe that doesn't belong since it's part of interpreting a number from a text stream not the exponent. :J)
2^-2
2^-(2)
2^-+2
all equate to 1/4, in the latter the (superfluous) "+" is handled correctly as well.
I've not played with the more complex expressions enough to decide about the ordering rule, although it does seem as though @creepydog's example breaks with the two precedence levels.
You can't try to make the operator stand on its own though...
2(^-)-2
Invalid expression. When calling a function or indexing a variable, use parentheses. Otherwise, check for mismatched delimiters.
Paul
2024-6-29
I don't see how ^- could ever be interpreted as a unary operator. If it actually were a single operator, it would have to have two operands (base and exponent) and therfore be a binary operator. But the comment above is the second time you've allowed for ^- being a unary operator as a possbility worthy of discussion.
Not sure where the last part about the "operator stand on its own" is going
2(^-)-2 % results in error
No operator in Matlab can stand on its own in that way. An operator has to operate on one or two operands. Putting the ^- inside the parentheses without any operands doesn't make sense.
I'm pretty sure the doc page was using parentheses, as in (^-), only as a stylstic separator in the text.
dpb
2024-6-30
编辑:dpb
2024-6-30
My bad using "unary" in two meanings--I just meant the interpretation of the two-character sequence as a single atomic operator; which I thought your previous response indicated you thought that rule was being broken.
My illustration is simply that the prior example was not actually the "^-" operator syntax with a negative exponent because it (the example) lacks the second negative sign to actually make the operator, the single negative sign is used to create a negative exponent value, but then the operator is simply "^", not "^-".
The last that illustrated that one cannot, in fact, segregate the operator sequence was done simply for the purpose of showing that if one hadn't tried; nothing else. I thought it possible from the stylistic form somebody might think it at least possible (and, actually, was curious myself as to what the parser would do with the superfluous set).
creepydog
2024-7-1
Interesting thoughts. If Matlab really treats ^- as a single operator (as the documentation says), how can an expression like a^-b not be ambiguous as it could also be parsed as the binary operator ^ followed by the unary operator -? (Although the results might be equal in this example.)
Another surprise:
>> a^b^c^d^e^f % ((((a^b)^c)^d)^e)^f
>> a^+b^c^d^e^f % (a^(((b^c)^d)^e))^f
>> a^++b^c^d^e^f % a^((((b^c)^d)^e)^f)
>> a^--b^c^d^e^f % a^((((b^c)^d)^e)^f)
@dpb: We have put our fingers sufficiently deep into the developers' wound. If they haven't been before, they're now certainly aware there's a problem. I'd prefer to wait and see. But anyone is welcome to submit a support request on their own.
dpb
2024-7-1
编辑:dpb
2024-7-1
@creepydog Indeed! :)
I think the Q? you raise is the point -- that the "-" gets interpreted first and eaten as the unary negation operator by the interpretation of the complete exponent expression before the parsing of the rest of the expression. Hence, the two "-" in succession example. That is, what isn't included in the explicit rules/description is that the unary negation is higher precedence and so it also doesn't necessarily parse from left to right and so just because a "-" follows a "^" doesn't necessarily produce the "^-" operator.
For the single case, I think the examples show that to be so; the chaining and more complex cases are still in doubt as to the rules, agreed.
I'm certainly glad to not be a compiler developer; I'd hate to have to try to implement these kinds of rules. :J
I think the supposition that all TMW will change may be the documentation to (at least try) to clarify what the actual implementation is will be the only action taken if a more specific support question is not raised.
In real life, the Answer of "use parentheses to ensure you get what you want" appears to be the only way to proceed and that to write compound expression without explicit grouping to clarify intent is risky and one better test the result if write such.
creepydog
2024-7-1
Well, Operator Precedence says that unary minus is level 4, i.e. lower than ^ (level 2) and ^- (level 3). But that shouldn't matter, I guess. If an operator is directly followed by another operator (like 2 ^ - 3), then that's an error unless the second one is unary --> that one must be evaluated first. Compare "5*-3" where the minus is evaluated first although times is usually stronger then minus.
(Disclaimer: As I said, I'm guessing. I'm not an expert on parsers or formal languages, not even a computer scientist.)
dpb
2024-7-1
What I had overlooked is that power with unary minus, plus or negation are defined in the operator precedence page as three-character operators such as (.^-). That means my before thinking there were only two character operators maximum was in error so by the precedence rules the sequence of those three characters should be parsed as the operation first rather than as it apparently is with the (-) having been interpreted as the uminus.
In 5*-3 example above, the minus is unary minus, "uminus", not "minus" which is level 4 while multiplication is level 5 so that -15 is the correct and expected result.
So, I agree, there are discrepancies from reality in the documentation and I also still agree with the conclusion to let sleeping dogs lie unless a really specific issue arises to create the need for an interpretation as I think we've been told TMW isn't going to change current behavior (nor, imo, should they owing to precedent and existing code base).
Paul
2024-7-1
I too don't think the current behavior will change, though it could probably be done over the course of time by providing mlint warnings, followed by run time warnings (maybe ?), followed by an actual change, all documented in the Release Notes over time. But I doubt it will happen.
However, that's no excuse for not fixing the documentation as it currently stands, which is inconsistent with actual behavior and inconsistent in terminology across different doc pages, for something as basic and important as operator definitions and precedence.
This comment accurately describes the situation, IMO.
dpb
2024-7-2
Too bad we can't see the actual design/specification as well as the user documentation... :J
Julian Hapke
2024-7-21
编辑:dpb
2024-7-21
You can see how MATLAB evaluates an expression by using the mtree function. It does indeed not recognize ^+ as it's own operator, but as soon as there is a unary operator inside an exponent, it influences the abstract syntax tree of the whole expression, e.g.:
```
dump(mtree('5 ^ 4 ^ 3 ^ 2'))
1=== * PRINT: 1/11
2=== * EXP: 1/11
3=== * EXP: 1/07
4=== * EXP: 1/03
5=== * INT: 1/01 (5)
6=== * INT: 1/05 (4)
7=== * INT: 1/09 (3)
8=== * INT: 1/13 (2)
dump(mtree('5 ^+ 4 ^ 3 ^ 2'))
1=== * PRINT: 1/12
2=== * EXP: 1/12
3=== * EXP: 1/03
4=== * INT: 1/01 (5)
5=== * UPLUS: 1/04
6=== * EXP: 1/08
7=== * INT: 1/06 (4)
8=== * INT: 1/10 (3)
9=== * INT: 1/14 (2)
```
5 ^ 4 ^ 3 ^ 2 == (((5 ^ 4) ^ 3) ^ 2)
and
5 ^+ 4 ^ 3 ^ 2 == (5 ^+ (4 ^ 3)) ^ 2
更多回答(0 个)
另请参阅
类别
在 Help Center 和 File Exchange 中查找有关 Startup and Shutdown 的更多信息
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!发生错误
由于页面发生更改,无法完成操作。请重新加载页面以查看其更新后的状态。
您也可以从以下列表中选择网站:
如何获得最佳网站性能
选择中国网站(中文或英文)以获得最佳网站性能。其他 MathWorks 国家/地区网站并未针对您所在位置的访问进行优化。
美洲
- América Latina (Español)
- Canada (English)
- United States (English)
欧洲
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)
亚太
- Australia (English)
- India (English)
- New Zealand (English)
- 中国
- 日本Japanese (日本語)
- 한국Korean (한국어)