演算子の優先順位
演算子の優先順位 (えんざんしのゆうせんじゅんい、英: precedence of operators) とは、演算子を利用しているような数式などが、どのように結び付いてグループ化されるべきであるかを、優先順位すなわち構文における優先度の強弱によって、あらかじめ暗黙に定めた規則である。数学ではしばしば、目的のために新しい演算子を導入することがあるが、そういう場合に優先順位があるのなら共通の暗黙の諒解は無いのだから規則を明示する必要がある。また、プログラミング言語では以下に述べるような規則の場合もあるが、APLのように優先順位は無く常に右から左に計算する、というような言語もあるといったように、その言語の設計者の考え方次第である。
算数(初等教育での数学)などが採用している規則では、乗除の演算子は加減の演算子より優先順位が高い。この規則により、2 + 3 × 4 という式における結び付きは、括弧で明示すると 2 + (3 × 4) となる。優先順位があることで、グループ化の明示のための記号である ( と )、{ と }、[ と ] などといった括弧の多用がある程度緩和される。
例えば、一般に多項式は、
といったような形で暗黙の優先順位を利用して書かれるが、もし優先順位が無かったら、
と書かねばならない。
一方で、演算子の優先順位があるために、括弧の多用が必要になる場合もある。前述の多項式をホーナー法で計算する場合、次の式のように変形するのであるが、
もし、演算子の優先順位が無く、左から右に計算するという規則だけだったならばこの式には括弧は不要である。
以上のように、演算子の優先順位というものは、そのような規則があったほうが良い場合のほうが比較的多いため、広く使われている暗黙の規則、という程度のものである。
数学史的には、代数学的記法が導入された際、乗法が加法より優先されるようになった[1]。したがって、3 + 4 × 5 = 4 × 5 + 3 = 23 となる。冪乗が16世紀から17世紀に導入されたとき、加法と乗法より優先されるとされ、底の右肩に冪指数を記述するようになった。したがって 3 + 52 = 28 であり、3 × 52 = 75 となる。演算順序を変えたい場合、かつては括線(オーバーラインまたは下線)を使っていた。今日では括弧を使って、先に評価すべき式の部分を明示的に囲む。したがって、乗法の前に加法を行うなら (2 + 3) × 4 = 20 などとし、冪乗の前に加法を行うなら (3 + 5)2 = 64 などとする。
概要
[編集]冒頭で述べた算数(初等教育での数学)での規則に加え、中等教育(日本の制度における高等学校などまでに相当)で使う演算子まで含め、ここでは説明する。なお、本来であれば2項演算子と単項演算子など、きちんと分類を考えて体系立った説明が必要だが、以下はそのようにはなっていない[2]。
これの意味するところは、例えばある数式の項の前後にそれぞれ異なる演算子があった場合、上記一覧の上の方(数字の若い方)にある演算子を先に適用すべきだということである。加法と乗法の交換法則および結合法則により、項の加算は任意の順序で可能であり、乗算も任意の順序で乗算可能だが、それらが混在している場合は標準の優先順位に従わなければならない。
除法を逆数による乗法として扱うことができ、減法を加法的逆元の加法として扱うことができる。すなわち、3 ÷ 4 = 3 • ¼ である。言い換えれば、3を4で割った商は、3と ¼ をかけた積と等しい。同様に 3 − 4 = 3 + (−4) であり、3と4の差は正の3と負の4の和と等しい。この理解により、 1 − 3 + 7 という式は1と負の3と7の和とみなすことができ、任意の順序で計算可能である。すなわち (1 − 3) + 7 = −2 + 7 = 5 と計算することもできるし、 (7 − 3) + 1 = 4 + 1 = 5 と計算することもできる。項の順序を入れ替える際、負号を常に3に付属させることが重要である。1 − (3 + 7) = 1 − 10 = − 9 と計算してはいけない。
平方根記号 √ は被開数(平方根を求める対象となっている数)をグループ化する記号を必要とする。通常使われるグループ化の記号は被開数の上にひかれる横線(括線)である。他の一般的関数は曖昧さを防ぐために入力を括弧で囲むが、入力が単項式等であれば括弧を省くこともある。例えば、sin x = sin(x) だが、sin x + y = sin(x) + y となる。なぜなら x + y が単項式でないためである。計算機では、一般に全ての関数の入力を括弧で囲む必要がある。
冪指数が積み重なっている場合、一番上の冪乗から計算する。
グループ化の記号は通常の演算子の優先順位を無効にすることができる。グループ化された部分は1つの式として扱うことができる。交換および分配法則を使えばグループ化記号を排除することができる。
例
[編集]水平な線分(括線)はグループ化記号として機能する。
読みやすくするため、通常の丸括弧 ( ) だけでなく、角括弧 [ ] や波括弧 { } をグループ化記号として使うことがある。例えば次のような式である。
問題点
[編集]以上のような規則は、しばしば混乱している。また、身近なコンピュータプログラム(ソフトウェア)などでの扱いが算数教育などでの扱いと違いがあったりする場合には、問題だと主張されることなどもある。
単項演算子としてのマイナス記号(負号)の扱い方はいくつかある。普通に書いた場合、−32 は −(32) = −9 を意味するが[3]、数式を扱うアプリケーションやプログラミング言語(特に Microsoft Office Excel やプログラミング言語bc)では単項演算子を二項演算子より優先しているためマイナス記号は冪乗より優先順位が高く、−32 は (−3)2 = 9 と解釈される[4]。意図した結び付きにならない場合は括弧を使って明示しなければならないし、優先順位をあてにせずに常に括弧を付けるという防御の姿勢をとることもある。
同様にスラッシュ記号 ('/') を数式で 1/2x のように使う際にも曖昧さがある。これを 1 ÷ 2 × x の意味で書いているなら、除算記号を分数を使った乗算で表現していると解釈でき、次のようになる。
すなわちこの解釈では、1/2x を 1/(2x) ではなく (1/2)x と等価とみなしていることになる。Wolfram AlphaやTI-89電卓では、括弧付きでない暗黙の乗法、括弧付きの暗黙の乗法いずれも演算子を明示した乗法と同じ扱いをしている。例えば、2x/2x、2(x)/2(x)、2*x/2*x はいずれも x2 となる[5]。 しかし書籍などでは、演算子を使わない暗黙の乗法を除法より優先すると解釈している場合がほとんどであり、1/2x は (1/2)x ではなく 1/(2x) と解釈している。これは 1 ÷ (2 × x) というよりもむしろ「2x 分の 1」と解釈すべきものである:
例えばフィジカル・レビュー誌の論文投稿要綱では、スラッシュで表される除法より乗法の優先順位が高いとしており[6]、ランダウとリフシッツの『理論物理学教程』やファインマンの教科書などでも同様の慣習が見られる[7]。
普通に考えて、曖昧な解釈が可能な数式は避けなければならないものである(その意味ではそもそも暗黙の規則である優先順位は、どちらかといえば無くても良いようにしたほうが良いものだと言える)。そのために、組版などで可能であればスラッシュではなく分数の形のほうがよく、無理ならば括弧を使う必要がある。
記憶術
[編集]欧米では、演算子の優先順位を覚えるための記憶術があるが、その頭字語を使った記憶術のせいで間違って覚えることがある。アメリカでは PEMDAS (Parentheses, Exponents, Multiplication, Division, Addition, Subtraction) という頭字語を使う。これを "Please Excuse My Dear Aunt Sally" という文で覚える(各単語の頭文字が所定の順序で並んでいる)。カナダでは BEDMAS、イギリスでは BIDMAS または BODMAS である。Bは Brackets、Iは Indices、Oは Orders を意味する。
これには乗法と除法、加法と減法に優先順位の上下関係があるかのような錯覚を与える欠点があり、そのように覚えてしまうと
という式を9ではなく5と計算してしまう可能性がある。「減法よりも加法が優先される」という解釈に基づく場合、 と解釈し正しい結果を得ることも可能である。
そのためニュージーランドでは、PEMA と教えている場合もある。この場合除法と減法が入っておらず、それぞれ乗法と加法と優先順位が等しいことを別途教える。
その他
[編集]階乗は感嘆符で表され、その直前(左)にある項に適用される。括弧が関わらない限り、階乗は優先的に計算される。ただし、23! は (23)! = 8! = 40320 を意味するが、23! は 26 = 64 となる。
冪乗が積み重なっている場合、上から計算する。すなわち次のようになる。
中黒を乗法の記号として使うこともあり、その場合は中黒より前の式全体と中黒より後の式全体の乗法を意味することがある[要出典]が、この記法は誤解されやすい。すなわち x + y • a + b は (x + y)(a + b) を意味することがあるが、この意味に解釈する場合後者の記法がより一般的である。同様に、1/a・b が 1/(a・b) を意味することがある。
電卓
[編集]電卓は機種によって演算子の優先順位が異なる場合がある。もっとも、電卓は数式を評価しているのではなく、単に操作順が数式に類似しているに過ぎず、本質的には、計算機としての内部の計算モデルの違いによるものである。
多くのいわゆる普通の電卓は、入力された順に計算する。例えば、
となる。これに対し、たいていの「関数電卓」は、加減算中の乗除算について別に計算できるバッファを持っており、
となる。Microsoft Windowsに添付されているアプリの電卓も、「普通の電卓」と「関数電卓」のモードに応じて、同様に異なった動作をする。
なお以上で説明した「関数電卓」は、いわゆる「標準方式」と呼ばれるもので、近年の関数電卓には数式をそのまま入力して評価する方式のものもある。
冪乗を計算できる電卓の場合、冪乗の結合性が左右どちらなのかは機種によって異なる。例えば、TI-92とTI-30XIIで a ^ b ^ c を計算した結果は異なる。
TI-92では冪乗は右結合性なので次のようになる。
- a ^ b ^ c = a ^ (b ^ c) =
一方TI-30XIIでは左結合性なので次のようになる。
- a ^ b ^ c = (a ^ b) ^ c =
また 1/2x のような式は、TI-82では 1/(2x) と解釈されるが、TI-83では (1/2)x と解釈される[8]。
−32 が −(32) ではなく (−3)2 と解釈されることがあることも上述の通りである。
6÷2(1+2)のような、括弧の前の省略された掛け算記号の優先順位についても関数電卓の解釈はまちまちである。カシオを例にとると、当初のモデルは省略された掛け算はされない掛け算より優先として1を返したが、fx-991ES/fx-570ES/fx-912ES/fx-370ESでは北米でのヒアリング結果に基づき、乗算記号を省略した掛け算は省略しない掛け算と同じ優先順位という考え方を採用して9を返すようになった。fx-993ES/fx-573ES/fx-913ES/fx-373ES以降のモデルは再び省略された掛け算はされない掛け算より優先の考え方に戻ったが、入力された式を6÷(2(1+2))に書き直した上で1を返すようになっている[9][10]。
プログラミング言語
[編集]プログラミング言語では、算術演算関係の演算子については、慣れの観点から、だいたいこれまで述べてきた規則と同様の規則であることが多いが、そうでないことも多い。それ以外の演算子については言語によってまちまちである。なかにはAPLやSmalltalkのように演算子の優先順位を持たない言語もある(APLではなんでも右から左へと評価していく。Smalltalkでは、なんでも左から右へと評価していく)。
なお、プログラミング言語における演算子の構文に関係する規則には、演算子の優先順位の他に「演算子の結合性」もある。結合法則#プログラミング言語を参照。
C言語
[編集]C言語における演算子の優先順位にまつわるトピックとして、ビット演算や論理演算の優先順位が比較演算より低いという点がある。a == b && c == d
のように、比較結果をさらに論理演算することは通常よくあるので妥当な設計と言えるが、ビット演算の優先順位の低さについては問題があり、例えば本来(x & mask) == y
と書くべきところを誤ってx & mask == y
と書いてしまうなど、しばしば言語仕様に関する理解度の低さやケアレスミスによるバグの原因となる。
デニス・リッチーは、この優先順位はC言語の仕様が変転していた黎明期(1970年代)において、B言語由来の仕様として、論理演算の演算子が分化していなかったことに原因があると説明している。具体的には、ビット演算と同じ記号列で、条件判定にまつわる式の文脈では論理演算の意味になる、という仕様であった。それにより例えば、
if (a==b & c==d) ...
というように書かれたコードが既に複数の場所で大量に存在していた(具体的には "several hundred kilobytes of source code, and maybe 3 installations....")。そのため、&&
と ||
を言語に追加した際に、それと同時に ==
と !=
の優先順位を下げる(下記の表で、現在の7から、10と11の間まで移動させる)ことで、既存のコードを壊してしまうことを恐れ(I had cold feet about the precedence problems.)そのようにはしなかった。結果として「ビット演算は必ず括弧で囲め」というイディオムで回避されているが、後から振り返ってみたならば、優先順位を変えていれば、そのようなイディオムは必要とならなかったのだし、そのほうが良かったのだろう、といったように述べている。[11][12]
なお、以上のような説明を述べたのは1982年のことで(K&Rの初版は1978年)、最初の標準規格であるC89よりも数年は前のことである。
C言語の影響を直接あるいは間接的に受けた言語のうち、C++、Perl、PHP、Java、C#、JavaScriptなどの多くの言語は演算子の優先順位をそのまま踏襲しているが、Goのように優先順位を修正したものもある。もともとC言語においてビット演算子の優先順位に関するケアレスミスが発生しやすいのは、真偽値型(ブーリアン型)がなく、論理演算の結果が整数型(int
型)になるためでもあるが、後発のJavaやC#では論理演算の結果がブーリアン型になり、さらにC++と違ってブーリアン型から整数型への暗黙変換(汎整数拡張)もなされないため、記述ミスは大抵のケースにおいて型の不一致によるコンパイルエラーが発生することで未然に発見される。なお、C/C++においても、x & mask == y
などと書いた場合は、記述ミスの可能性があることを警告してくれるコンパイラ[13]や静的コード解析ツール (lint) もある。
Cにおける演算子の優先順位は次の通りである。
優先度 | 演算子 | 機能 |
---|---|---|
1 | () [] -> . ++ -- |
関数呼出し演算子、配列要素、メンバへのアクセスなど後置演算子 |
2 | ! ~ - + * & sizeof type cast ++ -- |
前置の単項演算子など |
3 | * / % |
乗法、除法、剰余 |
4 | + - |
加法、減法 |
5 | << >> |
ビット単位のシフト |
6 | < <= > >= |
大小比較 |
7 | == != |
等価/非等価比較 |
8 | & |
ビット単位のAND |
9 | ^ |
ビット単位の排他的OR |
10 | | |
ビット単位のOR |
11 | && |
論理AND |
12 | || |
論理OR |
13 | ?: |
条件演算子 |
14 | = += -= *= /= %= &= |= ^= <<= >>= |
代入 |
15 | , |
コンマ演算子 |
例:
!A + !B
≡(!A) + (!B)
++A + !B
≡(++A) + (!B)
A + B * C
≡A + (B * C)
A || B && C
≡A || (B && C)
(A && B == C)
≡(A && (B == C) )
ソフトウェア開発者が二項演算子の優先順位について持っている知識の精度は、それら演算子がソースコードに出現する頻度と密接に関係していることが示されている[14]。
脚注
[編集]- ^ “Ask Dr. Math”. Math Forum (22 November 2000). 2012年3月5日閲覧。
- ^ 単項マイナスの導入の際にしばしばある説明では、数式は2項演算子から成るものというよりもむしろ、符号に先導された項の並びである、というように説明される場合もある。また多項式では乗算の記号の省略との関係や、後述する除算の扱いなど、そういったものを統合して説明し直すのはたいへん面倒である。
- ^ [Allen R. Angel, Elementary Algebra for College Students 8/E; Chapter 1, Section 9, Objective 3]
- ^ “Formula Returns Unexpected Positive Value”. Support.microsoft.com (15 August 2005). 2012年3月5日閲覧。
- ^ “2x÷2x - Wolfram|Alpha”. Wolframalpha.com. 2014年8月2日閲覧。
- ^ “Physical Review Style and Notation Guide”. American Physical Society. 2014年11月28日閲覧。
- ^ 例えば、『理論物理学教程』第1巻「力学」第3版には、hPz/2π (p. 22) という式があり、スラッシュを最後に評価するという意味で書かれている。
- ^ “Implied Multiplication Versus Explicit Multiplication on TI Graphing Calculators”. Texas Instruments Incorporated (16 January 2011). 2011年4月29日閲覧。
- ^ “関数電卓コラム 11/10/05 6÷2(1+2)=?”. 東海大学理学部 遠藤研究室. 2024年8月19日閲覧。
- ^ EEVblog 1479 - Is Your Calculator WRONG? - YouTube
- ^ http://www.lysator.liu.se/c/dmr-on-or.html
- ^ https://www.bell-labs.com/usr/dmr/www/chist.html
- ^ 例えばGCCやClangではコンパイルオプション
-Wparentheses
を付けることで、またMicrosoft Visual C++では C4554 の警告を有効にすることで記述ミスを発見できる。 - ^ "Developer beliefs about binary operator precedence" Derek M. Jones, CVu 18(4):14–21