名前空間
変種
操作

算術演算子

From cppreference.com
< c‎ | language

算術演算子は、オペランドに対して標準的な数学演算を適用します。

Operator 演算子名 結果
+ 単項プラス +a 昇格後の a の値
- 単項マイナス -a a の否定
+ 加算 a + b ab の加算
- 減算 a - b a から b を減算
* a * b ab の積
/ 除算 a / b ab で除算
% remainder a % b ab で割ったときの剰余
~ ビット単位 NOT ~a a のビット単位 NOT
& ビット単位 AND a & b ab のビット単位 AND
| ビット単位 OR a | b ab のビット単位 OR
^ ビット単位 XOR a ^ b ab のビット単位 XOR
<< ビット単位左シフト a << b b ビットだけ左シフトされた a
>> ビット単位右シフト a >> b b ビットだけ右シフトされた a

目次

[編集] オーバーフロー

符号なし整数演算は、nがその整数型におけるビット数である場合、常に 2n
を法として実行されます。たとえば、unsigned int の場合、UINT_MAX に 1 を加えると 0 になり、0 から 1 を引くと UINT_MAX になります。

符号付き整数算術演算でオーバーフローが発生した場合(結果が結果型に収まらない)、その動作は未定義です。表現の規則(通常は2の補数)に従ってラップアラウンドする可能性、一部のプラットフォームまたはコンパイラオプション(例: GCCおよびClangの-ftrapv)によりトラップされる可能性、またはコンパイラによって完全に最適化される可能性があります。

[編集] 浮動小数点環境

もし #pragma STDC FENV_ACCESSON に設定されている場合、すべての浮動小数点算術演算子は現在の浮動小数点丸め方向に従い、math_errhandling に指定されたとおりに浮動小数点算術エラーを報告します。ただし、静的初期化子の一部である場合(この場合、浮動小数点例外は発生せず、丸めモードは最近接になります)。

[編集] 浮動小数点演算の結合

もし #pragma STDC FP_CONTRACTOFF に設定されていない限り、すべての浮動小数点算術演算は、中間結果が無限の範囲と精度を持つかのように実行される可能性があります。つまり、式が正確に記述されたとおりに評価された場合に観測される丸め誤差や浮動小数点例外を省略する最適化が行われます。たとえば、(x*y) + z を単一の融合乗算加算CPU命令で実装したり、a = x*x*x*x;tmp = x*x; a = tmp*tmp として最適化したりすることが許可されます。

結合とは無関係に、浮動小数点算術の中間結果は、その型によって示されるものとは異なる範囲と精度を持つ場合があります。これはFLT_EVAL_METHOD を参照してください。

[編集] 単項算術演算

単項算術演算子式は、以下の形式になります。

+ expression (1)
- expression (2)
1) 単項プラス(昇格)
2) 単項マイナス(否定)
- 算術型の式

単項プラスと単項マイナスは、まずオペランドに整数昇格を適用し、その後、

  • 単項プラスは昇格後の値を返します
  • 単項マイナスは昇格後の値の否定を返します(ただし、NaNの否定は別のNaNになります)。

式の型は昇格後の型であり、値カテゴリは非l値です。

[編集] 注釈

単項マイナスは、INT_MINLONG_MIN、またはLLONG_MINに適用されると、符号付き整数オーバーフローにより未定義の動作を引き起こします(一般的な2の補数プラットフォームの場合)。

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)
1) 加算: lhsrhs は次のいずれかである必要があります。
  • 両方とも算術型(複素数および虚数を含む)である。
  • 一方が完全オブジェクト型へのポインタで、もう一方が整数型である。
2) 減算: lhsrhs は次のいずれかである必要があります。
  • 両方とも算術型(複素数および虚数を含む)である。
  • lhs が完全オブジェクト型へのポインタで、rhs が整数型である。
  • 両方とも、修飾子を無視して互換性のある型への完全オブジェクトポインタである。

[編集] 算術加算および減算

両方のオペランドが算術型の場合、

  • まず、通常の算術変換が実行されます。
  • 次に、変換後のオペランドの値は、数学の通常の規則に従って加算または減算されます(減算の場合、lhs から rhs が減算されます)。ただし、
  • 一方のオペランドがNaNの場合、結果はNaNです。
  • 無限大から無限大を引くとNaNになり、FE_INVALID が発生します。
  • 無限大と負の無限大の加算はNaNになり、FE_INVALID が発生します。

複素数および虚数の加算と減算は、次のように定義されます(結果の型は、両方のオペランドが虚数である場合は虚数、1つが実数で他が虚数の場合は複素数となることに注意してください。これは通常の算術変換によって指定されます)。

+ または - 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 に対して 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)
1) 乗算。lhsrhs算術型である必要があります。
2) 除算。lhsrhs算術型である必要があります。
3) 剰余。lhsrhs整数型である必要があります。

[編集] 乗算

二項演算子 * は、(通常の算術変換後)オペランドの乗算を、通常の算術定義に従って実行します。ただし、

  • 一方のオペランドがNaNの場合、結果はNaNです。
  • 無限大とゼロの乗算はNaNになり、FE_INVALID が発生します。
  • 無限大と非ゼロの乗算は、無限大になります(複素数の引数の場合でも)。

Cでは、少なくとも1つの無限大部分を持つ複素数は、他の部分が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_RANGEON に設定されている場合、その値は (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 simpler cases, take some from C++
 
 
   double complex z = (1 + 0*I) * (INFINITY + I*INFINITY);
// textbook formula would give
// (1+i0)(∞+i∞) ⇒ (1×∞ – 0×∞) + i(0×∞+1×∞) ⇒ NaN + I*NaN
// but C gives a complex infinity
   printf("%f + i*%f\n", creal(z), cimag(z));
 
// textbook formula would give
// cexp(∞+iNaN) ⇒ exp(∞)×(cis(NaN)) ⇒ NaN + I*NaN
// but C gives  ±∞+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です。
  • 最初のオペランドが複素数無限大で、2番目のオペランドが有限である場合、/ 演算子の結果は複素数無限大になります。
  • 最初のオペランドが有限で、2番目のオペランドが複素数無限大である場合、/ 演算子の結果はゼロになります。

Cでは、少なくとも1つの無限大部分を持つ複素数は、他の部分がNaNであっても無限大となるため、通常の算術規則は複素数同士の除算には適用されません。浮動小数点オペランドの他の組み合わせは、次の表に従います。

/ 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_RANGEON に設定されている場合、その値は (x+iy)/(u+iv) = [(xu+yv)+i(yu-xv)]/(u2
+v2
)
のように計算される場合があります。この場合、プログラマはオペランドの範囲を制限し、無限大を処理する責任を負います。

過度のオーバーフローを許可しないにもかかわらず、複素数除算は誤った浮動小数点例外を発生させる可能性があります(そうでなければ、オーバーフローしないバージョンを実装することは非常に困難です)。

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 の両方の動作は未定義です(これは2の補数システムでは INT_MIN%-1 が未定義であることを意味します)。

注意: 剰余演算子は浮動小数点型では機能しません。ライブラリ関数 fmod がその機能を提供します。

[編集] ビット論理演算

ビット算術演算子式は、以下の形式になります。

~ rhs (1)
lhs & rhs (2)
lhs | rhs (3)
lhs ^ rhs (4)
1) ビット単位 NOT
2) ビット単位 AND
3) ビット単位 OR
4) ビット単位 XOR

ここで、

lhs, rhs - 整数型の式

まず、演算子 &^、および | は、両方のオペランドに通常の算術変換を実行します。そして、演算子 ~ は、その唯一のオペランドに整数昇格を実行します。

次に、対応する二項論理演算子がビット単位で適用されます。つまり、結果の各ビットは、オペランドの対応するビットに適用される論理演算(NOT、AND、OR、またはXOR)に従って設定またはクリアされます。

注: ビット演算子は、ビットセットやビットマスクを操作するためによく使用されます。

注: 符号なし型(昇格後)の場合、式 ~E は、結果型で表現可能な最大値から元の値 E を引いたものに相当します。

#include <stdio.h>
#include <stdint.h>
 
int main(void)
{
    uint32_t a = 0x12345678;
    uint16_t mask = 0x00f0;
 
    printf("Promoted mask:\t%#010x\n"
           "Value:\t\t%#x\n"
           "Setting bits:\t%#x\n"
           "Clearing bits:\t%#x\n"
           "Selecting bits:\t%#010x\n"
           , mask
           , a
           , a | mask
           , a & ~mask
           , a & mask
    );
}

実行結果の例

Promoted mask:  0x000000f0
Value:          0x12345678
Setting bits:   0x123456f8
Clearing bits:  0x12345608
Selecting bits: 0x00000070

[編集] シフト演算子

ビットシフト演算子式は、以下の形式になります。

lhs << rhs (1)
lhs >> rhs (2)
1) rhs ビットだけ左シフトされた lhs
2) rhs ビットだけ右シフトされた lhs

ここで、

lhs, rhs - 整数型の式

まず、整数昇格が、各オペランドに個別に実行されます(注: これは、通常の算術変換を実行する他の二項算術演算子とは異なります)。結果の型は、昇格後の lhs の型です。

rhs が負であるか、または昇格された lhs のビット数以上である場合、動作は未定義です。

符号なし lhs の場合、LHS << RHS の値は、LHS * 2RHS
を、戻り値の型の最大値プラス1で割った余り(つまり、ビット単位の左シフトが実行され、宛先型からシフトアウトされたビットは破棄される)です。正の値を持つ符号付き lhs の場合、LHS << RHS の値は、昇格された lhs の型で表現可能であれば LHS * 2RHS
となります。それ以外の場合は未定義の動作です。

符号なし lhs および非負の値を持つ符号付き lhs の場合、LHS >> RHS の値は、LHS / 2RHS
の整数部分です。負の 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"   // overflow truncates high bits for unsigned numbers
           "0x10  << 10 = %#x\n",    // char is promoted to int
           ulong_num << 1, ulong_num << 63, c << 10);
    long long long_num = -1000;
    printf("-1000 >> 1 = %lld\n", long_num >> ONE);  // implementation defined
}

実行結果の例

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 += b
a -= b
a *= b
a /= b
a %= b
a &= b
a |= b
a ^= b
a <<= b
a >>= b

++a
--a
a++
a--

+a
-a
a + b
a - b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b

!a
a && b
a || b

a == b
a != b
a < b
a > b
a <= b
a >= b

a[b]
*a
&a
a->b
a.b

a(...)
a, b
(type) a
a ? b : c
sizeof


_Alignof
(C11 以降)
(C23まで)

alignof
(C23以降)

C++ドキュメント算術演算子について)
English 日本語 中文(简体) 中文(繁體)