C言語編 逆引き 文字列を実数に変換する

先頭へ戻る

このエントリーをはてなブックマークに追加

目的

"12.3"、"-12.3" のような文字列を、実数型の値に変換したいとします。 前者なら 12.3、後者なら -12.3 にします。

"12.3abc" のように、実数と関係の無い文字が登場した場合、その手前までで打ち切ります。 また、"abc12.3" の場合は、先頭部分が実数と関係の無い文字なので、変換に失敗します。

方法@(atof関数を使う)[C89〜]

C言語の標準ライブラリに含まれている atof関数を使うと、 文字列を double型の値に変換することができます。

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	printf("%f\n", atof("12.3"));
	printf("%f\n", atof("-12.3"));
	printf("%f\n", atof("+12.3"));
	printf("%f\n", atof("0"));
	printf("%f\n", atof("12.3abc"));
	printf("%f\n", atof("abc12.3"));
	
	return 0;
}

実行結果:

12.300000
-12.300000
12.300000
0.000000
12.300000
0.000000

この方法は手軽ですが、変換の失敗を検出する方法が無いという欠点があります。 変換できなかった場合の戻り値は 0.0 になりますから、"0" を変換した場合との区別が付きません。 変換の失敗を検出できる必要がある場合は、方法Aを使います。

方法A(strto〜系の関数を使う)[C89〜]

C言語の標準ライブラリには、atof関数以外にも、文字列から実数へ変換する各種関数が存在します。
変換後の型が double型の strtod関数、 float型の strtof関数、 long double型の strtold関数があります。 このうち、strtof関数、strtold関数は C99 で追加されたものです。

方法@で取り上げた atof関数と比べて、手軽さの面で大きく劣ります。 そのため、プログラムが長くなってしまうので、一旦、"12.3" だけを変換するプログラムを載せます。

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	double v;
	char* str;
	char* end;

	errno = 0;
	str = "12.3";
	v = strtod(str, &end);
	if (errno == ERANGE) {
		printf("error\n");
	}
	else if (str == end) {
		printf("can't convert\n");
	}
	else {
		printf("%f\n", v);
	}
	
	return 0;
}

実行結果:

12.300000

第1引数に変換したい文字列を指定します。
第2引数は、変換できなかった文字へのポインタを格納するポインタ変数を指定します。 これは不要なら NULL で構いませんが、変換の失敗を検出するためには指定が必要です。

2つの理由で、変換が失敗することがある点に注意が必要です。
1つには、変換後の値が、変換後の型で表現できないほど大きかったり、小さかったりするケースです。 このケースでは、errno に ERANGE がセットされるので、 これを使って調べることができます。
もう1つは、"abc" を変換しようとした場合のように、数値に変換できないケースです。 こちらは、第2引数をチェックすることで調べます。 変換できなかった文字へのポインタが、変換元の最初の文字を指していたら、 1文字も変換できなかったことを示しています。

このように、エラーの扱いを考慮すると、かなり長くなってしまいます。 そこで、使いやすいようにラップしたものを用意しておくと便利かも知れません。 一例として、次のようにできます。

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

/*
	文字列を double型へ変換する。

	str:    変換元の文字列。NULL は不可。
	result: 変換結果を受け取るアドレス。NULL は不可。
	        変換できなかった場合には、何も格納されない。
	戻り値: 変換できた場合は 0以外、変換できなかった場合は 0。
*/
static int stod(const char* str, double* result)
{
	double num;
	char* end;

	assert(str != NULL);
	assert(result != NULL);

	errno = 0;
	num = strtol(str, &end);
	if (errno == ERANGE){
		return 0;
	}
	else if (str == end) {
		return 0;
	}

	*result = num;
	return 1;
}

int main(void)
{
	double v;
	
	if (stod("12.3", &v)) {
		printf("%f\n", v);
	}
	else {
		printf("failed\n");
	}
	
	return 0;
}

実行結果:

12.300000

この場合、変換できたのかできなかったのかだけを判定できるようにしています。 成否は戻り値で判別するので、戻り値のパターンを増やせば、 変換できなかった理由を区別できるように対応することは可能です。


参考リンク

Cクイックリファレンス 第2版
 -- 標準ライブラリ関数の仕様について参考にした。

更新履歴

'2017/7/20 strto〜系の関数のエラーチェックについての文面を改めた。

'2017/6/19 新規作成。



逆引きのトップページへ

C言語編のトップページへ

Programming Place Plus のトップページへ