C言語で小数点以下を四捨五入する最も簡単な方法は,引数や戻り値の型に応じて以下のいずれかの関数を使用する方法です.
以下の関数はいずれも,指定された浮動小数点数を最も近い整数値 (中間にある場合は 0 から遠い方向) に丸める関数です.
関数 | ヘッダ | 備考 | |
---|---|---|---|
double | round(double x) | math.h | C99 |
float | roundf(float x) | math.h | C99 |
long double | roundl(long double x) | math.h | C99 |
long | lround(double x) | math.h | C99 |
long | lroundf(float x) | math.h | C99 |
long | lroundl(long double x) | math.h | C99 |
long long | llround(double x) | math.h | C99 |
long long | llroundf(float x) | math.h | C99 |
long long | llroundl(long double x) | math.h | C99 |
ただし,上記の関数はいずれも C99 で規定されている関数です. コンパイラが C99 に対応していなかったり,コーディング規約上 C99 を禁止している場合は,他の方法で四捨五入をする必要があります. そこで,ここでは round 系の関数を使わずに四捨五入を行う方法を 2 つ紹介します.
浮動小数点数に 0.5 を足してから小数点以下を切り捨てれば四捨五入することが可能です.
C言語には床関数 (floor function) を計算する floor という関数が用意されていますのでこれを利用します.
ただし,単純にこれをやってしまうと,負の浮動小数点数の場合に四捨五入することができません.負の浮動小数点数も扱う場合は場合分けが必要になります.
具体的には以下のように実装します.
/**
* 0.5を足してから切り捨てることで小数点以下を四捨五入する
* @param[in] x 四捨五入を行う浮動小数点数
* @return 四捨五入の結果
*/
double round_d(double x) {
if ( x > 0.0 ) {
return floor(x + 0.5);
} else {
return -1.0 * floor(fabs(x) + 0.5);
}
}
stdio.h には sprintf という関数が用意されています.ご存知の方も多いかと思いますが,この関数は printf 関数の類似関数です.printf 関数が標準出力にデータを出力するのに対して,sprintf 関数は文字配列にデータを書き込みます.
printf 系の関数は文字列書式に精度 (precision) を指定できます. この精度を超える値を変換する場合,適切な丸めが行われます. この「適切な丸め」というのが曲者で,JIS X 3010:2003 にはどうやって丸めるかの規定がされていません. しかし,四捨五入で丸める処理系であれば,この性質を利用して小数点以下を四捨五入をすることが可能です.
具体的には以下のように実装します.
/**
* sprintf関数を利用して小数点以下を四捨五入する
* @param[in] x 四捨五入を行う浮動小数点数
* @return 四捨五入の結果
*/
double round_s(double x) {
char s[1024] = {'\0'};
sprintf(s, "%.f", x);
return strtod(s, NULL);
}
なお,printf 系の関数の文字列書式は,精度を .* とすることで, 文字列書式の後ろに続く引数で精度を指定することができます.これを利用すると, 任意の小数点以下を四捨五入することが可能になります.
以下は小数点第 precision 桁以下を四捨五入するコードです.
/**
* sprintf関数を利用して, 任意の小数点以下を四捨五入する
* @param[in] x 四捨五入を行う浮動小数点数
* @param[in] precision 小数点以下の桁数
* @return 四捨五入の結果
*/
double round_s2(double x, int precision) {
char s[1024] = {'\0'};
sprintf(s, "%.*f", precision - 1, x);
return strtod(s, NULL);
}
この方法の欠点はなんといっても処理系によっては正常に動作しない可能性がある点です.
以下に浮動小数点数の四捨五入を round 関数で求めた場合,0.5 を足してから小数点以下を切り捨てることで求めた場合,sprintf 関数を使用して求めた場合のサンプルプログラムを示します.
/* header files */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/* macros */
#define N 4
/* functions */
double round_d(double x);
double round_s2(double x, int precision);
/* main */
int main(void) {
double x[] = {1.54, -1.55, 1.45, -1.44};
double r_round[N];
double r_round_d[N];
double r_round_s2[N];
int i;
for ( i = 0; i < N; i++) {
r_round[i] = round(x[i]);
r_round_d[i] = round_d(x[i]);
/* 小数点第1位を四捨五入 */
r_round_s2[i] = round_s2(x[i], 1);
}
/* 表示 */
for ( i = 0; i < N; i++) {
printf("%.2fを四捨五入した結果\n", x[i]);
printf("round: %.2f\n", r_round[i]);
printf("round_d: %.2f\n", r_round_d[i]);
printf("round_s2: %.2f\n", r_round_s2[i]);
printf("\n");
}
return EXIT_SUCCESS;
}
/**
* 0.5を足してから切り捨てることで小数点以下を四捨五入する
* @param[in] x 四捨五入を行う浮動小数点数
* @return 四捨五入の結果
*/
double round_d(double x) {
if ( x > 0.0 ) {
return floor(x + 0.5);
} else {
return -1.0 * floor(fabs(x) + 0.5);
}
}
/**
* sprintf関数を利用して, 任意の小数点以下を四捨五入する
* @param[in] x 四捨五入を行う浮動小数点数
* @param[in] precision 小数点以下の桁数
* @return 四捨五入の結果
*/
double round_s2(double x, int precision) {
char s[1024] = {'\0'};
sprintf(s, "%.*f", precision - 1, x);
return strtod(s, NULL);
}
サンプルプログラムの実行結果は以下のようになります.
1.54を四捨五入した結果 round: 2.00 round_d: 2.00 round_s2: 2.00 -1.55を四捨五入した結果 round: -2.00 round_d: -2.00 round_s2: -2.00 1.45を四捨五入した結果 round: 1.00 round_d: 1.00 round_s2: 1.00 -1.44を四捨五入した結果 round: -1.00 round_d: -1.00 round_s2: -1.00
たくさんあるC言語関連の書籍の中でも特に役に立った本です.よかったら参考にしてみてください.
C言語の実践的参考書.少々値段は張りますが初心者を脱しようとしている人は絶対に読むべきです.
文法だけでなく,コーディングスタイルやデバッグなど文字通り「実践的」なことが書かれているので非常にためになります.
オライリーの本は,読みにくい本が多いのですが本書はとても読みやすくオススメです.
ポインタの解説書としては最高の書籍です.
この1冊でポインタを完全に理解することができます.全くの初学者が読むには敷居が高いですが,入門書を読み終えた後に読むと非常に有益です.