C言語で計算を行う最初の一歩は、四則演算と剰余の理解です。
ここでは+, -, *, /, % の基本から、整数と浮動小数点の違い、演算子の優先順位、ありがちなエラーの回避法までを、サンプルコードと出力結果を添えて丁寧に解説します。
初心者の方でも段階的に読み進められるよう、意図が伝わる書き方と注意点をまとめました。
C言語の四則演算と剰余の基本 (+, -, *, /, %)
各演算子の意味と使い方
C言語の基本的な演算子は次の5つです。
式の左右にある値(オペランド)に対して演算を行い、新しい値を返します。
左辺値に直接代入されるわけではない点に注意してください。
演算子と用途の一覧です。
演算子 | 名前 | 例 | 説明 | 典型的な結果例 |
---|---|---|---|---|
+ | 加算 | 3 + 5 | 2つの値を足します | 8 |
- | 減算 | 9 - 2 | 左から右を引きます | 7 |
* | 乗算 | 4 * 6 | 掛け算をします | 24 |
/ | 除算 | 7 / 3 | 割り算をします | 整数同士は切り捨て(2)、どちらかが浮動小数点なら小数(2.333…) |
% | 剰余(余り) | 7 % 3 | 割り算の余りを求めます | 1 |
% は整数型に対してのみ使用できます。
浮動小数点(float, double, long double)には使えません。
サンプルコード
まずは基本の使い方を実行してみます。
整数型の計算と結果の表示です。
#include <stdio.h>
int main(void) {
// 変数の宣言と初期化
int a = 7;
int b = 3;
// 四則演算と剰余
int add = a + b; // 加算
int sub = a - b; // 減算
int mul = a * b; // 乗算
int div = a / b; // 整数同士の除算は小数点以下が切り捨て
int mod = a % b; // 余り
// 結果の表示
printf("a = %d, b = %d\n", a, b);
printf("a + b = %d\n", add);
printf("a - b = %d\n", sub);
printf("a * b = %d\n", mul);
printf("a / b = %d\n", div);
printf("a %% b = %d\n", mod); // % を出力するには %% と書く
return 0;
}
a = 7, b = 3
a + b = 10
a - b = 4
a * b = 21
a / b = 2
a % b = 1
整数の除算は小数を返さず、0方向へ切り捨てられることを最初に押さえましょう。
変数とリテラルで計算する
演算子は変数同士、変数とリテラル(定数)、リテラル同士のいずれにも使えます。
読みやすさのため、意味のある変数名と適切な型を選ぶと良いです。
#include <stdio.h>
int main(void) {
int width = 5; // 変数
int height = 3; // 変数
int area = width * height; // 変数同士
int expanded = area + 10; // 変数 + リテラル
int border = (width + 2) * (height + 2); // 括弧で意図を明確に
printf("area = %d\n", area);
printf("expanded = %d\n", expanded);
printf("border = %d\n", border);
return 0;
}
area = 15
expanded = 25
border = 35
型と計算結果の違い (整数演算/浮動小数点演算)
整数除算は0方向へ切り捨て (/)
C99以降、整数の除算は結果を0方向へ切り捨てます。
負の数を含む場合の挙動も確認しておきましょう。
#include <stdio.h>
int main(void) {
printf("7 / 3 = %d\n", 7 / 3); // 2
printf("-7 / 3 = %d\n", -7 / 3); // -2 (0方向へ切り捨て)
printf("7 / -3 = %d\n", 7 / -3); // -2
printf("-7 / -3 = %d\n", -7 / -3); // 2
return 0;
}
7 / 3 = 2
-7 / 3 = -2
7 / -3 = -2
-7 / -3 = 2
浮動小数点の除算結果
片方でも浮動小数点なら、結果は浮動小数点になります。
出力のフォーマット指定子に注意します。
#include <stdio.h>
int main(void) {
int a = 7, b = 3;
double r1 = a / b; // 整数同士で計算してから代入 → 2 が 2.000000 に
double r2 = (double)a / b; // 片方をdoubleに → 2.333333...
double r3 = 7.0 / 3; // リテラルをdoubleに → 2.333333...
printf("r1 = %f\n", r1);
printf("r2 = %.6f\n", r2);
printf("r3 = %.6f\n", r3);
return 0;
}
r1 = 2.000000
r2 = 2.333333
r3 = 2.333333
平均値や割合を計算するときは、必ず片方を浮動小数点にしてから割り算を行います。
剰余%は整数型のみ
% は整数型にしか使えません。
浮動小数点で余りを求めたいなら、fmod
(math.h)を使います。
// コンパイルエラーの例: 浮動小数点に % は使えない
// double x = 7.5;
// double y = 2.0;
// double r = x % y; // エラー
// 正しい方法 (fmod)
#include <stdio.h>
#include <math.h>
int main(void) {
double x = 7.5, y = 2.0;
double r = fmod(x, y); // 7.5 を 2.0 で割った余り: 1.5
printf("fmod(%.1f, %.1f) = %.1f\n", x, y, r);
return 0;
}
fmod(7.5, 2.0) = 1.5
型変換とキャストで意図を明確に
必要なときにだけ明示的キャストで意図を示すと、整数除算の罠を避けられます。
#include <stdio.h>
int main(void) {
int sum = 5 + 6 + 7; // 18
int count = 3;
// 悪い例: 先に整数除算してからdoubleに代入 → 6.000000
double avg_bad = sum / count;
// 良い例: 片方をdoubleへ明示的キャストしてから除算 → 6.000000
double avg_ok = (double)sum / count;
printf("avg_bad = %.6f\n", avg_bad);
printf("avg_ok = %.6f\n", avg_ok);
return 0;
}
avg_bad = 6.000000
avg_ok = 6.000000
この例では値が同じですが、sum や count が割り切れないときに差が出ます。
たとえば sum=5, count=2 なら avg_bad
は 2.000000、avg_ok
は 2.500000 です。
通常算術変換とint昇格
Cの式評価では通常算術変換(usual arithmetic conversions)と整数昇格(integer promotions)が自動で行われ、結果型が決まります。
- 概要
- 整数昇格:
char
やshort
は式中で少なくともint
に昇格してから計算します。 - 通常算術変換: 整数と浮動小数点を混在させると、より表現力の高い型(例:
double
)へ広がります。
- 整数昇格:
代表的な規則(簡略版)をまとめます。
オペランドの型の組み合わせ | 結果の型の例 | ポイント |
---|---|---|
int と int | int | 整数演算。/ は切り捨て |
int と double | double | int が double に変換される |
float と double | double | float は double に拡張 |
short と int | int | short は整数昇格で int に |
unsigned と signed | ルールにより広い側へ | 場合によりunsigned側に引き上げ |
実感するための短い例です。
#include <stdio.h>
int main(void) {
char a = 100, b = 27;
int s = a + b; // a, b は整数昇格され int で計算 → 127
double d = 2 + 5.0; // 2 は double に変換 → 7.0
printf("s = %d\n", s);
printf("d = %.1f\n", d);
return 0;
}
s = 127
d = 7.0
符号ありと符号なしの混在は思わぬ結果を生みます。
型を合わせる、必要に応じて明示的キャストを使うなど、意図を示すことが大切です。
演算子の優先順位と括弧の使い方
*, /, % は +, – より優先
*、/、% は +、- よりも先に評価されます。
意図しない順序で計算されないよう、結果が曖昧になり得る式には括弧を使います。
#include <stdio.h>
int main(void) {
int r1 = 2 + 3 * 4; // 3*4 が先 → 2 + 12 = 14
int r2 = (2 + 3) * 4; // 括弧が優先 → 5 * 4 = 20
printf("r1 = %d, r2 = %d\n", r1, r2);
return 0;
}
r1 = 14, r2 = 20
同優先順位は左結合
同じ優先順位の演算子(例: /
と *
)は左から右へ評価されます。
#include <stdio.h>
int main(void) {
int r = 20 / 4 * 2; // (20 / 4) * 2 = 5 * 2 = 10
printf("r = %d\n", r);
return 0;
}
r = 10
読みやすさ重視で括弧を使う
「他人が読んで一度で意図がわかるか」を基準に括弧を使うと保守性が上がります。
特に複数の演算子が混在する式や、単位が異なる値を混ぜる式では明示的に括弧でグループ化しましょう。
#include <stdio.h>
int main(void) {
int base = 100, tax_percent = 10, discount = 15;
// 税込み後に割引するのか、割引後に税をかけるのかで違いが出る
int price_after_tax_then_discount = (base * (100 + tax_percent) / 100) - discount;
int price_after_discount_then_tax = ((base - discount) * (100 + tax_percent) / 100);
printf("tax→discount: %d\n", price_after_tax_then_discount);
printf("discount→tax: %d\n", price_after_discount_then_tax);
return 0;
}
tax→discount: 95
discount→tax: 93
よくあるエラーと落とし穴の回避
ゼロ除算を防ぐ
0で割ると整数では未定義動作(クラッシュ等)になります。
浮動小数点では inf
や nan
が生じます。
割る前に必ず0チェックを行いましょう。
#include <stdio.h>
int main(void) {
int x = 10, y = 0;
if (y == 0) {
printf("割り算できません(ゼロ除算)\n");
} else {
printf("x / y = %d\n", x / y);
}
double a = 1.0, b = 0.0;
// 浮動小数点はゼロ除算で inf/nan になることがある
if (b == 0.0) {
printf("浮動小数点のゼロ除算は避けましょう\n");
} else {
printf("a / b = %f\n", a / b);
}
return 0;
}
割り算できません(ゼロ除算)
浮動小数点のゼロ除算は避けましょう
オーバーフロー/アンダーフローに注意
符号付き整数のオーバーフローは未定義動作です。
事前に範囲をチェックするか、より広い型を使います。
#include <stdio.h>
#include <limits.h> // INT_MAX, LLONG_MAX
int main(void) {
int a = INT_MAX - 5;
int b = 10;
// 加算の安全チェック(単純化した例)
if (b > 0 && a > INT_MAX - b) {
printf("オーバーフローの可能性があります\n");
} else {
printf("a + b = %d\n", a + b);
}
long long x = LLONG_MAX / 2;
long long y = 3;
if (y != 0 && x > LLONG_MAX / y) {
printf("乗算でオーバーフローの可能性があります\n");
} else {
printf("x * y = %lld\n", x * y);
}
return 0;
}
オーバーフローの可能性があります
乗算でオーバーフローの可能性があります
浮動小数点では「アンダーフロー(極小値へ丸め)」や「丸め誤差」が生じます。
必要に応じて double
や long double
を使い、比較は許容誤差を設けます。
負の数と剰余%の符号
C99以降、剰余の符号は被除数(左オペランド)と同じです。
#include <stdio.h>
int main(void) {
printf("-7 %% 3 = %d\n", -7 % 3); // -1
printf("7 %% -3 = %d\n", 7 % -3); // 1
printf("-7 %% -3 = %d\n", -7 % -3); // -1
return 0;
}
-7 % 3 = -1
7 % -3 = 1
-7 % -3 = -1
1/2 と 1.0/2 の違い
整数同士の割り算は切り捨て、小数が混じれば小数計算になります。
#include <stdio.h>
int main(void) {
printf("1 / 2 = %d\n", 1 / 2); // 0
printf("1.0 / 2 = %.1f\n", 1.0 / 2); // 0.5
return 0;
}
1 / 2 = 0
1.0 / 2 = 0.5
浮動小数点の精度誤差に注意
2進表現では0.1や0.2を正確に表せないため、0.1 + 0.2 が 0.3 と厳密には一致しません。
比較には許容誤差(イプシロン)を用います。
#include <stdio.h>
#include <math.h>
int main(void) {
double x = 0.1 + 0.2;
double y = 0.3;
double eps = 1e-12;
printf("0.1 + 0.2 = %.17f\n", x);
printf("0.3 = %.17f\n", y);
if (fabs(x - y) < eps) {
printf("ほぼ等しいとみなせます\n");
} else {
printf("一致しません\n");
}
return 0;
}
0.1 + 0.2 = 0.30000000000000004
0.3 = 0.29999999999999999
ほぼ等しいとみなせます
金額や誤差が許されない分野では整数(最小単位で持つ)で扱う設計も有効です。
まとめ
四則演算と剰余は、C言語のあらゆるプログラムの土台です。
整数の除算は0方向へ切り捨て、%は整数限定、混在型では通常算術変換が働くという3点をまず押さえましょう。
さらに、優先順位は*, /, % が +, – より先、曖昧さは括弧で解消する、ゼロ除算やオーバーフローを事前に回避する、といった基本も重要です。
浮動小数点の誤差を理解し、必要に応じてキャストで意図を明確にすれば、初心者でも安全で読みやすい数式が書けるようになります。