文字列として受け取った数値を実際に計算で使える浮動小数点数に変換する場面は、ユーザー入力や設定ファイルの読み込みなどで頻繁に登場します。
C言語ではatof
関数が手軽に使えますが、使いどころと注意点を理解していないと意図しない結果を招きます。
ここでは初心者でも迷わないように、基本から実践、そして落とし穴まで丁寧に解説します。
C言語のatofの基本
何をする関数か
atof
(ASCII to floating)は、数値を表す文字列をdouble
型に変換する関数です。
先頭の空白や符号を処理し、可能な範囲で数値を読み取ります。
読み取りは「数字として解釈できない文字に出会ったところで停止」します。
シグネチャ
関数の宣言は次の通りです。
- 戻り値:
double
- 引数: 変換するヌル終端文字列の先頭ポインタ
const char *nptr
動作の概要
- 先頭の空白(スペース、タブなど)は読み飛ばします。
- 符号
+
や-
を受け付けます。 - 整数部、
.
(小数点)、小数部、指数部e
/E
などを解釈します。 - 解釈できない文字に到達したらそこまでを数値として扱います。
必要なヘッダ
atof
は<stdlib.h>
で宣言されています。
使用するソースファイルの先頭でインクルードします。
#include <stdlib.h> // atof
#include <stdio.h>
int main(void) {
const char *s = "3.14";
double x = atof(s); // 文字列をdoubleへ変換
printf("x = %f\n", x);
return 0;
}
対応する書式
典型的に解釈される書式を例で示します。
小数点は標準のCロケールでは.
(ピリオド)です。
例の文字列 | 主な解釈 | 備考 |
---|---|---|
12.34 | 12.34 | 通常の小数 |
.5 | 0.5 | 先頭の0は省略可 |
5. | 5.0 | 末尾の0は省略可 |
1e3 | 1000 | 指数表記(e/E) |
-2.5e-2 | -0.025 | 符号と指数の組み合わせ |
” +42″ | 42 | 先頭空白と+ |
3.14abc | 3.14 | 数字以外の文字で読み取り停止 |
0x1.8p+1 | 3.0 | 16進浮動小数(C99以降)。初心者は無理に使わないでOK |
実装によってはinf
/infinity
、nan
も受け付けますが、移植性と可読性の観点で初心者は避けるのが無難です。
戻り値
- 変換に成功した場合は、読み取れた範囲の数値を
double
で返します。 - 変換可能な部分がまったく無い場合は
0.0
を返します。 - 注意:
0.0
は「本当に0だった」場合と「失敗した」場合の両方で返るため、エラー判定には使えません。
atofの使い方
文字列を浮動小数点数(double)に変換
最も基本的な使い方です。
配列の複数の文字列をatof
で変換して表示します。
#include <stdio.h>
#include <stdlib.h>
// 基本的なatofの使い方デモ
int main(void) {
const char *inputs[] = {
"12.34", "0.001", "1e3", ".5", "5.", " 42.0", "-3.14", "+6.022e23"
};
for (size_t i = 0; i < sizeof(inputs)/sizeof(inputs[0]); ++i) {
const char *s = inputs[i];
double val = atof(s); // 文字列からdoubleへ変換
// %.17g は double を簡潔かつ誤解の少ない形で表示しやすい
printf("\"%s\" -> %.17g\n", s, val);
}
return 0;
}
"12.34" -> 12.34
"0.001" -> 0.001
"1e3" -> 1000
".5" -> 0.5
"5." -> 5
" 42.0" -> 42
"-3.14" -> -3.14
"+6.022e23" -> 6.022e+23
表示フォーマットの補足
printf
で浮動小数を見やすく表示するには%f
や%g
を使います。
%g
は不要な0や指数表記を自動選択するため、サンプルのようなデモに向いています。
空白や符号(+/-)の扱い
atof
は先頭の空白を無視し、符号も処理します。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char *a = " -12.34";
const char *b = "+5.0e2";
const char *c = " \t +0.75";
printf("\"%s\" -> %f\n", a, atof(a)); // -12.340000
printf("\"%s\" -> %f\n", b, atof(b)); // 500.000000
printf("\"%s\" -> %f\n", c, atof(c)); // 0.750000
return 0;
}
" -12.34" -> -12.340000
"+5.0e2" -> 500.000000
" +0.75" -> 0.750000
数字以外の文字が混ざる場合
数字以外の文字が出た地点で読み取りを停止します。
残りの文字は無視されます。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char *s1 = "3.14abc";
const char *s2 = "123xyz456";
const char *s3 = "abc";
const char *s4 = "1.2.3";
printf("\"%s\" -> %.6f\n", s1, atof(s1)); // 3.140000
printf("\"%s\" -> %.6f\n", s2, atof(s2)); // 123.000000
printf("\"%s\" -> %.6f\n", s3, atof(s3)); // 0.000000 (変換失敗でも0)
printf("\"%s\" -> %.6f\n", s4, atof(s4)); // 1.200000 (2つ目の'.'で停止)
return 0;
}
"3.14abc" -> 3.140000
"123xyz456" -> 123.000000
"abc" -> 0.000000
"1.2.3" -> 1.200000
「無視される残りの文字」が存在してもエラーにはなりません。
この仕様が便利な一方で、厳密な入力検証には向かないことを覚えておくと安心です。
atofの注意点
エラー判定はできない
atof単体では「変換に失敗したのか、0なのか」を区別できません。
また、オーバーフローやアンダーフローが発生しても、errno
や未消費文字位置を直接知る手段がありません。
入力検証やエラーハンドリングが必要な場面ではより厳密なstrtod
を使ってください(後述)。
0と失敗の区別に注意
次の例では、"0"
も"abc"
も0.0
になります。
出力だけではどちらが来たのか判断できません。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const char *inputs[] = { "0", "0.0", "0abc", "abc" };
for (size_t i = 0; i < sizeof(inputs)/sizeof(inputs[0]); ++i) {
printf("\"%s\" -> %f\n", inputs[i], atof(inputs[i]));
}
return 0;
}
"0" -> 0.000000
"0.0" -> 0.000000
"0abc" -> 0.000000
"abc" -> 0.000000
この曖昧さがあるため、入力の妥当性確認を伴う場面でatof
を使うのは避けるのが安全です。
入力はヌル終端の文字列にする
atof
はヌル終端(末尾が'\0'
)の文字列を前提に動作します。
ファイルや標準入力から読み込む場合はfgets
など必ずヌル終端を保証する関数を使いましょう。
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char buf[64];
// fgetsは終端に'#include <stdio.h>
#include <stdlib.h>
int main(void) {
char buf[64];
// fgetsは終端に'\0'を付けるので安全(バッファサイズ-1文字まで)
if (fgets(buf, sizeof(buf), stdin) != NULL) {
// 入力末尾の改行が残る場合は取り除く
for (char *p = buf; *p != '\0'; ++p) {
if (*p == '\n') { *p = '\0'; break; }
}
double x = atof(buf);
printf("入力: \"%s\" -> %f\n", buf, x);
}
return 0;
}
'を付けるので安全(バッファサイズ-1文字まで)
if (fgets(buf, sizeof(buf), stdin) != NULL) {
// 入力末尾の改行が残る場合は取り除く
for (char *p = buf; *p != '#include <stdio.h>
#include <stdlib.h>
int main(void) {
char buf[64];
// fgetsは終端に'\0'を付けるので安全(バッファサイズ-1文字まで)
if (fgets(buf, sizeof(buf), stdin) != NULL) {
// 入力末尾の改行が残る場合は取り除く
for (char *p = buf; *p != '\0'; ++p) {
if (*p == '\n') { *p = '\0'; break; }
}
double x = atof(buf);
printf("入力: \"%s\" -> %f\n", buf, x);
}
return 0;
}
'; ++p) {
if (*p == '\n') { *p = '#include <stdio.h>
#include <stdlib.h>
int main(void) {
char buf[64];
// fgetsは終端に'\0'を付けるので安全(バッファサイズ-1文字まで)
if (fgets(buf, sizeof(buf), stdin) != NULL) {
// 入力末尾の改行が残る場合は取り除く
for (char *p = buf; *p != '\0'; ++p) {
if (*p == '\n') { *p = '\0'; break; }
}
double x = atof(buf);
printf("入力: \"%s\" -> %f\n", buf, x);
}
return 0;
}
'; break; }
}
double x = atof(buf);
printf("入力: \"%s\" -> %f\n", buf, x);
}
return 0;
}
途中でヌル終端しないバイト列を渡すと未定義動作になり、クラッシュや誤変換の原因になります。
厳密な変換はstrtod
厳密に検証したい場合はstrtod
を使います。
strtod
は読み取り終了位置を返すため、未消費文字があるかを判定でき、errno
で範囲エラーも検出できます。
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h> // isspace
#include <errno.h> // errno, ERANGE
// 文字列全体が正しい小数(先頭空白可、末尾空白可)か検証しつつ変換
int parse_double_strict(const char *s, double *out) {
errno = 0;
// 先頭空白をスキップ(任意)
while (isspace((unsigned char)*s)) { ++s; }
char *end = NULL;
double val = strtod(s, &end);
if (s == end) {
// 一文字も読み取れなかった => 変換失敗
return 0;
}
if (errno == ERANGE) {
// 範囲外(オーバーフロー/アンダーフロー)
return 0;
}
// 末尾空白をスキップして、完全に読み切ったか確認
while (isspace((unsigned char)*end)) { ++end; }
if (*end != '#include <stdio.h>
#include <stdlib.h>
#include <ctype.h> // isspace
#include <errno.h> // errno, ERANGE
// 文字列全体が正しい小数(先頭空白可、末尾空白可)か検証しつつ変換
int parse_double_strict(const char *s, double *out) {
errno = 0;
// 先頭空白をスキップ(任意)
while (isspace((unsigned char)*s)) { ++s; }
char *end = NULL;
double val = strtod(s, &end);
if (s == end) {
// 一文字も読み取れなかった => 変換失敗
return 0;
}
if (errno == ERANGE) {
// 範囲外(オーバーフロー/アンダーフロー)
return 0;
}
// 末尾空白をスキップして、完全に読み切ったか確認
while (isspace((unsigned char)*end)) { ++end; }
if (*end != '\0') {
// 数字の後にゴミが残っている
return 0;
}
*out = val;
return 1;
}
int main(void) {
const char *tests[] = { "123.456", "3.14abc", "abc", "1e309", " -0.25 " };
for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); ++i) {
double v = 0.0;
int ok = parse_double_strict(tests[i], &v);
if (ok) {
printf("\"%s\" -> OK: %.17g\n", tests[i], v);
} else {
printf("\"%s\" -> NG\n", tests[i]);
}
}
return 0;
}
') {
// 数字の後にゴミが残っている
return 0;
}
*out = val;
return 1;
}
int main(void) {
const char *tests[] = { "123.456", "3.14abc", "abc", "1e309", " -0.25 " };
for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); ++i) {
double v = 0.0;
int ok = parse_double_strict(tests[i], &v);
if (ok) {
printf("\"%s\" -> OK: %.17g\n", tests[i], v);
} else {
printf("\"%s\" -> NG\n", tests[i]);
}
}
return 0;
}
"123.456" -> OK: 123.456
"3.14abc" -> NG
"abc" -> NG
"1e309" -> NG
" -0.25 " -> OK: -0.25
実運用ではatof
よりstrtod
を優先し、必要に応じて「入力全体を消費したか」「範囲エラーはないか」をチェックするのが堅牢です。
まとめ
atofは「ざっくり変換する」ための簡便な関数で、先頭空白や符号、指数表記を扱えます。
一方でエラー判定ができず、0と失敗の区別がつかないという大きな制約があります。
ヌル終端の文字列を渡すこと、読み取りが途中で止まってもエラーにならないことを理解したうえで、入力検証が必要な場面ではstrtod
を使うのが安全です。
用途に応じてatof
とstrtod
を使い分け、予期せぬバグや誤動作を避けていきましょう。