Thinkers'Studio
JavaとC言語の自習ツール
文字列をdouble型に変換する atof関数の自前処理解説
 atof は、<stdlib.h> に定義されている関数で、文字列を渡すと double型の浮動小数点数に変換して返します。
 形式は次の通りです。
  double atof( const char *s )    s を double に変換して返す

 今回は、カーニハン&リッチーの「プログラミング言語C」に示されている atof関数の処理手続きを紹介します。
 以前に紹介した atoi の自前処理例 に小数部の処理を加えたものです。

atof 関数の自前処理例 】  プログラムを見て関数の仕様を読み取ってみましょう。
#include <ctype.h>

double atof( char s[] ) {
    double val, power;
    int i, sign;

    for( i = 0; isspace( s[i] ); i++ )     //先頭の空白を読み飛ばす
        ;
    sign = ( s[i] == '-' ) ? -1 : 1;       //符号を保存する
    if( s[i] == '-' || s[i] == '+' )       //符号を飛ばす
        i++;
    for( val = 0.0; isdigit( s[i] ); i++ ) //s[i]が数字のあいだ valへ
        val = 10.0 * val + ( s[i] - '0' );
    if( s[i] == '.' )                      //ピリオドを飛ばす
        i++;
    //小数部も整数としてvalへ、後で割る値をpowerに求める
    for( power = 1.0; isdigit( s[i] ); i++ ) {
        val = 10.0 * val + ( s[i] - '0' );
        power *= 10.0;
    }
    return sign * val/power;               //小数に戻し符号を反映
}

atof の処理内容をたどると

 【 たとえば s が "123.456" だとすると 】
  まず val に 123 が入り(val は 123.0)、ピリオドを飛ばして 456 がその続きに入ります(val は 123456.0)。
  power は初期値が 1.0 で、これを小数点以下のひと桁を処理するたびに 10倍しています(power は 1000.0)。
  return 文の式で power で割って返すので、戻り値は 123.456 となります。

atof の処理概要

 つまり、"." 以降の数字もすべて整数部として val に入れ、最後に小数部の桁数分で割って、小数点の位置を設定しているわけです。この割り算に使う変数 power は、小数部の桁を val に保存するときに一緒に求めます。
 val = 10.0 * val + ( s[i] - '0' );
 の処理についてですが、10.0 を掛けて先ほどまでの値をひとつ上の位に押し上げ、s[i] の数字を"数値"として末の位に保存しています(atoi の自前処理例 にも説明があります)。

atof 自前処理関数の仕様

 上の atof は、次のような仕様であることが分かります。
  • 先頭の空白は無視する
  • 先頭の符号に対応する ( +か指定なしなら正、-なら負)
  • 小数点以下がない場合にも対応する
  • 数値に変換できたところまでの値で返す (指数表現(1.2E3などの書き方)には対応しない)
  • 空白でも符号でもない最初の文字が数字でないときは 0.0 を戻す
 ※ ライブラリ関数には、atof を整備した strtod という関数があります。 両者の違いについては、また別の Tips で。

(今回のクイズです)
 上の atof を次のように呼び出すと、どんな結果が表示されるでしょうか?
 printf( "%f\n", atof( "0.543f" ) );
 printf( "%f\n", atof( "  3." ) );
 printf( "%f\n", atof( "ABC" ) );
 printf( "%f\n", atof( "1e3" ) );
   (答えは、次回のC言語の Tips で ・・・)
前回のクイズの答え: (前回の問題を見る
曖昧さを無くすために、次の2点を改善しました。
 ・関数の戻り値の型を省略すると int型ですが、これを省略せずに書きました。
 ・暗黙の型変換でなく、キャストを使って明示的型変換をした結果を戻すようにしました。
int shishagonyu( double x )
{
   return (int)(x < 0.0 ? x-0.5 : x+0.5);
}