C言語で最も近い整数に四捨五入したいときは、標準ライブラリのround系とlround系を使います。
どちらも「0.5は0から遠い方へ」という分かりやすい規則で丸めますが、戻り値の型やオーバーフロー時の扱いが異なります。
この記事では、roundとlroundの違い、0.5や負数の丸め方、printfの指定子、範囲の注意点まで、初心者の方にも分かりやすく丁寧に解説します。
四捨五入
roundの使い方
小数のまま「四捨五入後の数値」を得たいときはroundを使います。
戻り値は浮動小数点型です。
関数プロトタイプ
#include <math.h> に宣言されています。
double round(double x);float roundf(float x);long double roundl(long double x);
基本的な使い方
0.5ちょうどのときは0から遠い方(いわゆる「四捨五入」)に丸めます。
戻り値は引数と同じ浮動小数点族の型なので、整数にキャストせずに使えます。
整数が必要なら後からキャストしますが、キャスト時は範囲に注意します。
バリアントの使い分け
- 倍精度の通常用途:
round(double) - 単精度で十分:
roundf(float) - 拡張精度が必要:
roundl(long double)
lroundの使い方
最終的な戻り値を整数型として受け取りたい場合はlroundを使います。
ルールはroundと同じですが、戻り値が整数です。
関数プロトタイプ
long int lround(double x);long int lroundf(float x);long int lroundl(long double x);- さらに大きい整数が必要なら
long long int llround(double x);などもあります。
オーバーフロー時の扱い
丸め結果がlongに収まらない場合、lroundは浮動小数点例外FE_INVALIDを発生させ、戻り値は実装定義(未規定)になります。
必要に応じて<fenv.h>で例外を検出してください。
範囲チェックを行わずに結果を使うのは危険です。
戻り値の違い
roundは浮動小数点を返し、lroundは整数を返すという点が最も大きな違いです。
丸め規則(0.5は0から遠い方へ)は同じです。
以下に要点をまとめます。
| 関数群 | 引数型 | 戻り値型 | 0.5の扱い | 範囲外時(オーバーフロー) | 主な用途 |
|---|---|---|---|---|---|
| round/roundf/roundl | 浮動小数点 | 浮動小数点 | 0から遠い方へ | なし(NaNはNaNのまま) | 小数のまま結果が欲しい場合 |
| lround/lroundf/lroundl | 浮動小数点 | long int | 0から遠い方へ | FE_INVALIDを発生、戻り値は未規定 | 整数が必要な場合 |
| llround/llroundf/llroundl | 浮動小数点 | long long int | 0から遠い方へ | FE_INVALIDを発生、戻り値は未規定 | 64bit整数が必要な場合 |
roundの戻り値は浮動小数点なのでとても大きな値では整数を正確に表現できないことがあります(倍精度doubleで正確に表せる整数は2^53−1まで)。
一方lroundは整数を返しますが、longの範囲を超えるとFE_INVALIDが発生します。
ヘッダはmath.h
これらの関数は#include <math.h>で宣言されています。
入出力のために#include <stdio.h>、範囲定数のために#include <limits.h>、例外検出に#include <fenv.h>を併用すると便利です。
コンパイルとリンク
Unix系(GCC/Clang)では数学ライブラリへのリンクが必要です。
忘れやすいポイントなので気を付けてください。
- 例:
gcc main.c -std=c11 -O2 -lm
WindowsのMSVCでは-lmは不要です。
0.5の丸め規則
0.5は0から遠い方へ
roundとlroundはいずれも「0.5ちょうどのときは0から遠い方」に丸めます。
これは「四捨五入」の直感に合う規則です。
具体例を挙げると以下のようになります。
- 5 → 3 に丸める
- 5 → 2 に丸める
- -1.5 → -2 に丸める
- -2.5 → -3 に丸める
- 5 → 1、-0.5 → -1
なぜこうなるか
0に近い側へ寄せると統計的に偏りが出ることがあるため、Cのround/lroundは「0から遠い側(away from zero)」に統一されています。
なお、銀行丸め(偶数丸め)ではありません。
負数の丸め方
負数でも正数と同じく「最も近い整数、0.5は0から遠い方」に丸めます。
したがって、-1.2は-1、-1.5は-2、-1.8は-2になります。
他の関数との違い(ceil/floor/trunc)
floor(-1.2)は-2(負方向へ切り下げ)ceil(-1.2)は-1(正方向へ切り上げ)trunc(-1.8)は-1(0方向へ小数切り捨て)round(-1.5)/lround(-1.5)は-2(0から遠い方へ)
四捨五入をしたいならroundまたはlroundを選び、単純な切り上げ・切り下げ・小数切り捨ては別の関数を使います。
使い方サンプルと注意点
まずはroundで四捨五入の基本を確認します。
戻り値はdoubleです。
// round_sample.c
// round()で四捨五入する基本例。戻り値はdouble(浮動小数点)です。
#include <stdio.h>
#include <math.h>
int main(void) {
double values[] = {
0.49, 0.5, 0.51,
1.49, 1.5, 1.51,
-0.49, -0.5, -0.51,
-1.49, -1.5, -1.51,
2.5, -2.5
};
size_t n = sizeof(values) / sizeof(values[0]);
for (size_t i = 0; i < n; ++i) {
double x = values[i];
double r = round(x); // 0.5は0から遠い方へ丸める
// 入力値は小数2桁、結果は小数0桁(整数相当)で表示
printf("round(%6.2f) = %6.0f\n", x, r);
}
return 0;
}
round( 0.49) = 0
round( 0.50) = 1
round( 0.51) = 1
round( 1.49) = 1
round( 1.50) = 2
round( 1.51) = 2
round( -0.49) = 0
round( -0.50) = -1
round( -0.51) = -1
round( -1.49) = -1
round( -1.50) = -2
round( -1.51) = -2
round( 2.50) = 3
round( -2.50) = -3
0.5は必ず0から遠い側に進みます。
表示では%.0fを使い、浮動小数点の結果を整数のように見せています。
整数として使いたい場合はlroundを検討してください。
次にlroundで整数を受け取りつつ、long範囲外の扱いも確認します。
<fenv.h>で例外を検出します。
// lround_sample.c
// lround()で四捨五入し、longに収まらない場合のFE_INVALIDを検出する例
#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <fenv.h>
// 浮動小数点環境へアクセスすることをコンパイラに伝える
#pragma STDC FENV_ACCESS ON
int main(void) {
// 典型値と境界の近く、NaN/Infも含める
double xs[] = {
2.5, -2.5, 0.5, -0.5,
(double)LONG_MAX - 0.4, (double)LONG_MAX + 0.4, // 境界近く(正側)
(double)LONG_MIN + 0.4, (double)LONG_MIN - 0.4, // 境界近く(負側)
INFINITY, -INFINITY, NAN
};
size_t n = sizeof(xs) / sizeof(xs[0]);
for (size_t i = 0; i < n; ++i) {
double x = xs[i];
// 例外フラグをクリア
feclearexcept(FE_ALL_EXCEPT);
// isfiniteで無限大/NaNを事前に弾くことも有効
if (!isfinite(x)) {
printf("lround(%g): 入力が有限ではありません\n", x);
continue;
}
long r = lround(x); // 0.5は0から遠い方へ。結果はlong。
// 範囲外ならFE_INVALIDが立つ(戻り値は未規定)
if (fetestexcept(FE_INVALID)) {
printf("lround(%g): FE_INVALID(結果がlongの範囲外)\n", x);
} else {
printf("lround(%g) = %ld\n", x, r);
}
}
return 0;
}
lround(2.5) = 3
lround(-2.5) = -3
lround(0.5) = 1
lround(-0.5) = -1
lround(9.22337e+18): FE_INVALID(結果がlongの範囲外)
lround(9.22337e+18): FE_INVALID(結果がlongの範囲外)
lround(-9.22337e+18): FE_INVALID(結果がlongの範囲外)
lround(-9.22337e+18): FE_INVALID(結果がlongの範囲外)
lround(inf): 入力が有限ではありません
lround(-inf): 入力が有限ではありません
lround(nan): 入力が有限ではありません
環境によりlongのビット幅や表示は異なるため、数値表記やどの値でFE_INVALIDになるかは出力が多少変わります。
例外が検出できていることが重要です。
printfの指定子
型とフォーマット指定子の組み合わせを間違えると未定義動作になり得ます。
次のポイントを覚えておくと安全です。
roundの戻り値(倍精度double)は%fや%.0fで表示します。lroundの戻り値(long)は%ldで表示します。llroundの戻り値(long long)は%lldで表示します。
簡単な例を示します。
#include <stdio.h>
#include <math.h>
int main(void) {
double x = 2.5;
printf("round: %.0f\n", round(x)); // double → %.0f
printf("lround: %ld\n", lround(x)); // long → %ld
printf("llround:%lld\n", llround(x)); // long long → %lld
return 0;
}
round: 3
lround: 3
llround:3
Windows(MSVC)ではlongが32bit、LinuxやmacOS(多くの64bit環境)ではlongが64bitという違いがあります。
フォーマット指定子は同じ%ldですが、表現できる範囲が異なります。
longの範囲に注意
lroundの結果がlongに収まるとは限りません。
安全に扱うには次の点に注意します。
<limits.h>のLONG_MIN/LONG_MAXで範囲を把握します。<fenv.h>でFE_INVALID例外を検出します(上のサンプル参照)。- さらに広い範囲が必要なら
llroundや整数型long longを検討します。 - 入力が
NaNやInfinityのときは整数にできないため、isfiniteで事前に弾くと堅牢です。
doubleは約2^53までしか整数を正確に表せない点にも注意してください。
非常に大きな桁の入力は、そもそも丸め前から誤差を含んでいる可能性があります。
まとめ
C言語で四捨五入を行うには、浮動小数点のまま結果が欲しければround、整数が欲しければlroundを使います。
いずれも0.5は0から遠い方へ丸めるため、-2.5→-3のように負数でも直感的です。
printfの指定子(%.0f、%ld、%lld)を正しく選び、longの範囲を超える可能性がある場合はFE_INVALIDの検出やllroundの利用を検討してください。
最後に、Unix系では-lmでリンクすることも忘れないようにしましょう。
