Arithmetic operators
算術演算子は標準的な数学的演算をそのオペランドに適用します。
|
このセクションは不完全です
理由:このテーブルおよび複数のトピックを扱う他のテーブルに対して、より汎用的な目次を検討すること |
| 演算子 | 演算子名 | 例 | 結果 |
|---|---|---|---|
| + | 単項プラス | + a | プロモーション後の a の値 |
| - | 単項マイナス | - a | a の負の値 |
| + | 加算 | a + b | a と b の加算 |
| - | 減算 | a - b | a から b を減算 |
| * | 乗算 | a * b | a と b の積 |
| / | 除算 | a / b | a を b で除算 |
| % | 剰余 | a % b | a を b で除算した剰余 |
| ~ | bitwise NOT | ~a | a のビット単位NOT |
| & | bitwise AND | a & b | a と b のビット単位AND |
| | | bitwise OR | a | b | a と b のビット単位OR |
| ^ | bitwise XOR | a ^ b | a と b のビット単位XOR |
| << | bitwise left shift | a << b | a を b だけ左シフト |
| >> | bitwise right shift | a >> b | a を b だけ右シフト |
目次 |
オーバーフロー
符号なし整数演算は常に
2
n
を法として実行されます。ここでnはその整数のビット数です。例:
unsigned
int
の場合、
UINT_MAX
に1を加算すると
0
となり、
0
から1を減算すると
UINT_MAX
となります。
符号付き整数の算術演算がオーバーフローした場合(結果が結果の型に収まらない)、その動作は未定義です:表現の規則(一般的には2の補数)に従ってラップアラウンドする可能性があります。一部のプラットフォームやコンパイラオプション(例:GCCとClangの
-ftrapv
)によりトラップする可能性があります。あるいはコンパイラによって完全に
最適化されて除去される
可能性もあります。
浮動小数点環境
#pragma STDC FENV_ACCESS
が
ON
に設定されている場合、すべての浮動小数点算術演算子は現在の浮動小数点
丸め方向
に従い、
math_errhandling
で指定されている通りに浮動小数点算術エラーを報告します。ただし、
静的初期化子
の一部である場合を除きます(その場合、浮動小数点例外は発生せず、丸めモードは最近接偶数丸めとなります)。
浮動小数点縮約
#pragma STDC FP_CONTRACT
が
OFF
に設定されていない限り、すべての浮動小数点演算は、中間結果が無限の範囲と精度を持つかのように実行されてもよい。つまり、式が記述された通りに正確に評価された場合に観測される丸め誤差や浮動小数点例外を省略する最適化が許可される。例えば、
(
x
*
y
)
+
z
の実装に単一の融合積和演算CPU命令を使用することや、
a
=
x
*
x
*
x
*
x
;
を
tmp
=
x
*
x
;
a
=
tmp
*
tmp
として最適化することが可能である。
契約とは無関係に、浮動小数点演算の中間結果は、その型で示される範囲と精度とは異なる範囲と精度を持つ場合があります。詳細は FLT_EVAL_METHOD を参照してください。
単項算術演算
単項算術演算子式の形式は以下の通りです
+
式
|
(1) | ||||||||
-
式
|
(2) | ||||||||
| expression | - | 任意の 算術型 の式 |
単項プラスおよび単項マイナスはまず被演算子に 整数プロモーション を適用し、その後
- 単項プラスは昇格後の値を返す
- 単項マイナスは昇格後の値の負数を返す(ただしNaNの負数は別のNaNとなる)
式の型は昇格後の型であり、 value category は非左辺値です。
注記
単項マイナス演算子は、典型的な(2の補数)プラットフォームにおいて、 INT_MIN 、 LONG_MIN 、または LLONG_MIN に適用された場合、符号付き整数オーバーフローにより未定義動作を引き起こします。
C++では、単項演算子
+
は配列や関数などの他の組み込み型でも使用できますが、C言語ではそうではありません。
#include <stdio.h> #include <complex.h> #include <limits.h> int main(void) { char c = 'a'; printf("sizeof char: %zu sizeof int: %zu\n", sizeof c, sizeof +c); printf("-1, where 1 is signed: %d\n", -1); // Defined behavior since arithmetic is performed for unsigned integer. // Hence, the calculation is (-1) modulo (2 raised to n) = UINT_MAX, where n is // the number of bits of unsigned int. If unsigned int is 32-bit long, then this // gives (-1) modulo (2 raised to 32) = 4294967295 printf("-1, where 1 is unsigned: %u\n", -1u); // Undefined behavior because the mathematical value of -INT_MIN = INT_MAX + 1 // (i.e. 1 more than the maximum possible value for signed int) // // printf("%d\n", -INT_MIN); // Undefined behavior because the mathematical value of -LONG_MIN = LONG_MAX + 1 // (i.e. 1 more than the maximum possible value for signed long) // // printf("%ld\n", -LONG_MIN); // Undefined behavior because the mathematical value of -LLONG_MIN = LLONG_MAX + 1 // (i.e. 1 more than the maximum possible value for signed long long) // // printf("%lld\n", -LLONG_MIN); double complex z = 1 + 2*I; printf("-(1+2i) = %.1f%+.1f\n", creal(-z), cimag(-z)); }
出力例:
sizeof char: 1 sizeof int: 4 -1, where 1 is signed: -1 -1, where 1 is unsigned: 4294967295 -(1+2i) = -1.0-2.0
加算演算子
二項加算算術演算子式の形式は以下の通りです
lhs
+
rhs
|
(1) | ||||||||
lhs
-
rhs
|
(2) | ||||||||
-
- 両方が arithmetic types を持つ(複素数型と虚数型を含む)
- 一方が完全オブジェクト型へのポインタで、他方が整数型を持つ
算術加算と減算
両方のオペランドが 算術型 を持つ場合、
- まず、 通常の算術変換 が実行されます
- その後、変換後のオペランドの値は数学の通常の規則に従って加算または減算されます(減算の場合、 rhs が lhs から減算されます)。ただし以下を除きます:
-
- 一方のオペランドがNaNの場合、結果はNaNとなる
- 無限大から無限大を減算するとNaNとなり、 FE_INVALID が発生する
- 無限大と負の無限大を加算するとNaNとなり、 FE_INVALID が発生する
複素数と虚数の加算および減算は以下のように定義されます(両方のオペランドが虚数の場合は結果の型が虚数となり、一方のオペランドが実数でもう一方が虚数の場合は複素数となることに注意してください。これは通常の算術変換によって指定されます):
| + または - | u | iv | u + iv |
|---|---|---|---|
| x | x ± u | x ± iv | (x ± u) ± iv |
| iy | ±u + iy | i(y ± v) | ±u + i(y ± v) |
| x + iy | (x ± u) + iy | x + i(y ± v) | (x ± u) + i(y ± v) |
// work in progress // note: take part of the c/language/conversion example
ポインタ演算
-
ポインタ
PがインデックスIの配列要素を指している場合、
-
-
P
+
N
および
N
+
P
は、同じ配列のインデックス
I+Nの要素を指すポインタです -
P
-
N
は、同じ配列のインデックス
I-Nの要素を指すポインタです
-
P
+
N
および
N
+
P
は、同じ配列のインデックス
この動作は、元のポインタと結果のポインタの両方が同じ配列の要素、またはその配列の終端の次の位置を指している場合にのみ定義されます。pが配列の最初の要素を指しているときにp-1を実行すると未定義動作となり、一部のプラットフォームでは失敗する可能性があることに注意してください。
-
ポインタ
P1がインデックスIの配列要素(または末尾の次)を指し、P2が同じ配列のインデックスJの要素(または末尾の次)を指す場合、
-
- P1 - P2 は値が I - J に等しく、型は ptrdiff_t となる(これは符号付き整数型であり、通常は宣言可能な最大オブジェクトのサイズの半分の大きさを持つ)
この動作は、結果が ptrdiff_t に収まる場合にのみ定義されます。
配列の要素ではないオブジェクトへのポインタは、ポインタ演算の目的において、サイズ1の配列の最初の要素へのポインタとして扱われます。
// work in progress int n = 4, m = 3; int a[n][m]; // VLA of 4 VLAs of 3 ints each int (*p)[m] = a; // p == &a[0] p = p + 1; // p == &a[1] (pointer arithmetic works with VLAs just the same) (*p)[2] = 99; // changes a[1][2]
乗算演算子
二項乗算算術演算子式の形式は以下の通りです。
lhs
*
rhs
|
(1) | ||||||||
lhs
/
rhs
|
(2) | ||||||||
lhs
%
rhs
|
(3) | ||||||||
- まず、 通常の算術変換 が実行されます。その後...
乗算
二項演算子 * は、そのオペランドの乗算を(通常の算術変換後)通常の算術定義に従って実行します。ただし、
- 一方のオペランドがNaNの場合、結果はNaNとなる
- 無限大とゼロの乗算はNaNを生成し、 FE_INVALID が発生する
- 無限大と非ゼロ値の乗算は無限大を生成する(複素数の引数に対しても)
C言語では、少なくとも1つの無限部分を持つ任意の complex value は、他の部分がNaNであっても無限大となるため、通常の算術規則は複素数同士の乗算には適用されません。浮動小数点オペランドの他の組み合わせについては、以下の表に従います:
| * | u | iv | u + iv |
|---|---|---|---|
| x | xu | i(xv) | (xu) + i(xv) |
| iy | i(yu) | −yv | (−yv) + i(yu) |
| x + iy | (xu) + i(yu) | (−yv) + i(xv) | 特殊規則 |
無限値の取り扱いに加えて、複素数の乗算は中間結果のオーバーフローが許可されません。ただし、
#pragma STDC CX_LIMITED_RANGE
が
ON
に設定されている場合は例外で、この場合の値は
(x+iy)×(u+iv) = (xu-yv)+i(yu+xv)
によって計算され、プログラマーがオペランドの範囲制限と無限値の処理に対する責任を負うものとします。
不当なオーバーフローを禁止しているにもかかわらず、複素数の乗算は偽の浮動小数点例外を発生させる可能性があります(そうでなければ、オーバーフローしないバージョンを実装することは非常に困難です)
#include <stdio.h> #include <stdio.h> #include <complex.h> #include <math.h> int main(void) { // TODO より単純なケース、C++からいくつか採用 double complex z = (1 + 0*I) * (INFINITY + I*INFINITY); // 教科書の公式では次のようになる // (1+i0)(∞+i∞) ⇒ (1×∞ – 0×∞) + i(0×∞+1×∞) ⇒ NaN + I*NaN // しかしC言語では複素無限大が得られる printf("%f + i*%f\n", creal(z), cimag(z)); // 教科書の公式では次のようになる // cexp(∞+iNaN) ⇒ exp(∞)×(cis(NaN)) ⇒ NaN + I*NaN // しかしC言語では ±∞+i*nan が得られる double complex y = cexp(INFINITY + I*NAN); printf("%f + i*%f\n", creal(y), cimag(y)); }
出力例:
inf + i*inf inf + i*nan
除算
二項演算子
/
は、最初のオペランドを2番目のオペランドで除算します(通常の算術変換後)。ただし、以下の例外を除き、通常の算術定義に従います。
- 通常の算術変換後の型が整数型の場合、結果は代数商(分数ではない)、 実装定義の方向に丸められる (C99まで) ゼロ方向に切り捨てられる (C99以降)
- 一方のオペランドがNaNの場合、結果はNaNとなる
-
第1オペランドが複素無限大で第2オペランドが有限の場合、
/演算子の結果は複素無限大となる -
第1オペランドが有限で第2オペランドが複素無限大の場合、
/演算子の結果はゼロとなる
C言語では、少なくとも1つの無限部分を持つ任意の complex value は、たとえ他の部分がNaNであっても無限大として扱われるため、通常の算術規則はcomplex-complex除算には適用されません。浮動小数点オペランドの他の組み合わせについては、以下の表に従います:
| / | u | iv |
|---|---|---|
| x | x/u | i(−x/v) |
| iy | i(y/u) | y/v |
| x + iy | (x/u) + i(y/u) | (y/v) + i(−x/v) |
無限大の取り扱いに加えて、複素数の除算は中間結果をオーバーフローさせることが許可されていません。ただし、
#pragma STDC CX_LIMITED_RANGE
が
ON
に設定されている場合は例外で、この場合の値は
(x+iy)/(u+iv) = [(xu+yv)+i(yu-xv)]/(u
2
+v
2
)
によって計算される可能性があります。この場合、プログラマはオペランドの範囲を制限し、無限大の処理に対応する責任を負います。
不当なオーバーフローを禁止しているにもかかわらず、複素数の除算は偽の浮動小数点例外を発生させる可能性があります(そうでなければ、オーバーフローしないバージョンを実装することは非常に困難です)
第2オペランドがゼロの場合、動作は未定義です。ただし、IEEE浮動小数点演算がサポートされており、浮動小数点除算が実行されている場合は除きます。
- 非ゼロの数値を ±0.0 で除算すると、正しい符号の無限大が得られ、 FE_DIVBYZERO が発生する
- 0.0 を 0.0 で除算すると NaN が得られ、 FE_INVALID が発生する
剰余
二項演算子 % は、最初のオペランドを2番目のオペランドで除算した余りを返します(通常の算術変換後)。
剰余の符号は、商
a/b
が結果の型で表現可能である場合、
(
a
/
b
)
*
b
+
a
%
b
==
a
が成り立つように定義されます。
第2オペランドがゼロの場合、動作は未定義です。
商
a/b
が結果の型で表現できない場合、
a/b
および
a%b
の両方の動作は未定義です(つまり、
INT_MIN
%-
1
は2の補数システムでは未定義です)
注: 剰余演算子は浮動小数点型では機能しません。その機能はライブラリ関数 fmod によって提供されます。
ビット単位論理
ビット単位算術演算子式の形式は以下の通りです
~
rhs
|
(1) | ||||||||
lhs
&
rhs
|
(2) | ||||||||
lhs
|
rhs
|
(3) | ||||||||
lhs
^
rhs
|
(4) | ||||||||
タグ内のテキストは翻訳対象外
- C++専門用語(lhs, rhs)は翻訳対象外
- 元のフォーマットを完全に保持
- ビット演算子(~, &, |, ^)はC++構文として保持
ここで
| lhs , rhs | - | 整数型の式 |
まず、演算子 & 、 ^ 、および | は両オペランドに対して 通常の算術変換 を実行し、演算子 ~ は唯一のオペランドに対して 整数プロモーション を実行します。
次に、対応する二項論理演算子がビット単位で適用されます。つまり、結果の各ビットは、オペランドの対応するビットに論理演算(NOT、AND、OR、またはXOR)を適用して設定またはクリアされます。
注意: ビット演算子は、ビットセットとビットマスクを操作するために一般的に使用されます。
注: 符号なし型(プロモーション後)の場合、式 ~E は、結果型で表現可能な最大値から元の E の値を引いた値と等価です。
出力例:
Promoted mask: 0x000000f0 Value: 0x12345678 Setting bits: 0x123456f8 Clearing bits: 0x12345608 Selecting bits: 0x00000070
シフト演算子
ビット単位シフト演算子の式は以下の形式を持ちます
lhs
<<
rhs
|
(1) | ||||||||
lhs
>>
rhs
|
(2) | ||||||||
ただし
| lhs , rhs | - | 整数型の式 |
まず、 整数昇格 が各オペランドに対して個別に実行されます(注: これは他の二項算術演算子とは異なり、それらはすべて通常の算術変換を実行します)。結果の型は昇格後の lhs の型となります。
rhs が負の値であるか、または昇格後の lhs のビット数以上である場合、動作は未定義です。
符号なし
lhs
の場合、
LHS << RHS
の値は
LHS * 2
RHS
の値を、戻り値の型の最大値+1を法として縮小したものとなります(つまり、ビット単位の左シフトが実行され、宛先型からシフトアウトされたビットは破棄されます)。非負の値を持つ符号付き
lhs
の場合、
LHS << RHS
の値は、
LHS * 2
RHS
が
lhs
の昇格型で表現可能な場合はその値となり、それ以外の場合の動作は未定義です。
符号なし
lhs
および非負の値を持つ符号付き
lhs
の場合、
LHS >> RHS
の値は
LHS / 2
RHS
の整数部分です。負の
LHS
の場合、
LHS >> RHS
の値は実装定義であり、ほとんどの実装では算術右シフトが実行されます(結果が負のままとなるように)。したがって、ほとんどの実装では、符号付き
LHS
の右シフトは、新しい上位ビットを元の符号ビット(つまり、非負の場合は0、負の場合は1)で埋めます。
#include <stdio.h> enum {ONE=1, TWO=2}; int main(void) { char c = 0x10; unsigned long long ulong_num = 0x123; printf("0x123 << 1 = %#llx\n" "0x123 << 63 = %#llx\n" // オーバーフローにより符号なし数値の上位ビットが切り捨てられる "0x10 << 10 = %#x\n", // charはintに昇格される ulong_num << 1, ulong_num << 63, c << 10); long long long_num = -1000; printf("-1000 >> 1 = %lld\n", long_num >> ONE); // 実装定義 }
出力例:
0x123 << 1 = 0x246 0x123 << 63 = 0x8000000000000000 0x10 << 10 = 0x4000 -1000 >> 1 = -500
参考文献
- C17規格 (ISO/IEC 9899:2018):
-
- 6.5.3.3 単項算術演算子 (p: 64)
-
- 6.5.5 乗法演算子 (p: 66)
-
- 6.5.6 加法演算子 (p: 66-68)
-
- 6.5.7 ビット単位シフト演算子 (p: 68)
-
- 6.5.10 ビット単位AND演算子 (p: 70)
-
- 6.5.11 ビット単位排他的OR演算子 (p: 70)
-
- 6.5.12 ビット単位包括的OR演算子 (p: 70-71)
- C11規格 (ISO/IEC 9899:2011):
-
- 6.5.3.3 単項算術演算子 (p: 89)
-
- 6.5.5 乗法演算子 (p: 92)
-
- 6.5.6 加法演算子 (p: 92-94)
-
- 6.5.7 ビット単位シフト演算子 (p: 94-95)
-
- 6.5.10 ビット単位AND演算子 (p: 97)
-
- 6.5.11 ビット単位排他的OR演算子 (p: 98)
-
- 6.5.12 ビット単位包括的OR演算子 (p: 98)
- C99規格 (ISO/IEC 9899:1999):
-
- 6.5.3.3 単項算術演算子 (p: 79)
-
- 6.5.5 乗法演算子 (p: 82)
-
- 6.5.6 加法演算子 (p: 82-84)
-
- 6.5.7 ビット単位シフト演算子 (p: 84-85)
-
- 6.5.10 ビット単位AND演算子 (p: 87)
-
- 6.5.11 ビット単位排他的OR演算子 (p: 88)
-
- 6.5.12 ビット単位包括的OR演算子 (p: 88)
- C89/C90標準 (ISO/IEC 9899:1990):
-
- 3.3.3.3 単項算術演算子
-
- 3.3.5 乗法演算子
-
- 3.3.6 加法演算子
-
- 3.3.7 ビット単位シフト演算子
-
- 3.3.10 ビット単位AND演算子
-
- 3.3.11 ビット単位排他的OR演算子
-
- 3.3.12 ビット単位包括的OR演算子
関連項目
| 一般的な演算子 | ||||||
|---|---|---|---|---|---|---|
| 代入 |
インクリメント
デクリメント |
算術 | 論理 | 比較 |
メンバー
アクセス |
その他 |
|
a
=
b
|
++
a
|
+
a
|
!
a
|
a
==
b
|
a
[
b
]
|
a
(
...
)
|
|
C++ ドキュメント
for
算術演算子
|