goo blog サービス終了のお知らせ 

プログラミングのメモ帳(C/C++/HSP)

日々のプログラミングで気づいた点や小技集を紹介します。(Windows 10/XP/Vista、VC2017、HSP)

数式文字列の四則演算(実数値)

2012年03月24日 17時28分00秒 | アルゴリズム関連

数式文字列を評価して実数値を得る方法を紹介します。(戻る)

整数値から実数値の対応

前回は「数式文字列の四則演算(整数値)」を紹介しました。
今回は小数も扱える実数値タイプの紹介ですが、単純に前回のソースコードの long 型から double 型に修正しただけです。
また long 型から double 型を扱うための関数や printf() の書式制御文字列を修正してます。
以下に long 型から double 型以外で修正が必要な個所を紹介します。

return strtol( g_parse, (char**)&g_parse, 10 );
return strtod( g_parse, (char**)&g_parse );

数値文字列の評価である funcValue() 関数の strtol から strtod に修正します。

if ( isdigit(*g_parse) || (*g_parse == '+') || (*g_parse == '-') ){
if ( isdigit(*g_parse) || (*g_parse == '+') || (*g_parse == '-') || (*g_parse == '.') ){

括弧・数値の評価である funcFactor() 関数で小数点を表す「.」から始まる数字を正しく評価できるように条件式を1つ増やします。

CASE '%':       g_parse++; ans %= funcFactor();
CASE '%': g_parse++; ans = fmod( ans, funcFactor() );

乗算・除算・剰余の評価である funcMulDiv() 関数で余りを求める演算子 % は浮動小数点では使えません。
そこで余りを求める関数の fmod に修正してます。

printf( "%s = %d\n\n", buff, calcExpress(buff) );
printf( "%s = %f\n\n", buff, calcExpress(buff) );

数式文字列の入力処理がある main() 関数で printf の書式制御文字列を %d から %f に修正します。

数式文字列の評価関数

//------------------------------------------------
// 関数のプロトタイプ宣言
//------------------------------------------------
extern double calcExpress( const char string[] );
extern   long calcErrCode();
  • calcExpress() が数式文字列を評価して結果を返す関数です。
  • calcErrCode() は数式文字列の評価で発生したエラーコードを取得する関数です。

下請け関数

//------------------------------------------------
// 関数のプロトタイプ宣言
//------------------------------------------------
static double funcValue();
static double funcFactor();
static double funcMulDiv();
static double funcAddSub();
  • funcValue() は実数値を評価します。
  • funcFactor() は括弧・数字を評価します。
  • funcMulDiv() は乗算・除算・剰余を評価します。
  • funcAddSub() は加算・減算を評価します。

再帰処理の呼び出し

数式文字列を評価するとき、次の順で関数を呼び出します。

  1. calcExpress() で解析ポインタの初期化を行う。
  2. funcAddSub() で加算・減算の計算を行う。
  3. funcMulDiv() で乗算・除算・剰余の計算を行う。
  4. funcFactor() で括弧・数字・プラス・マイナスの計算を行う。
  5. funcValue() で数値文字列を実数値に変換する。

括弧の評価は、括弧内の数式文字列を評価するので funcAddSub() 関数を再帰呼び出しします。
この方法だけで加算、減算、乗算、除算、剰余、括弧の優先順位を考慮して計算を行います。
また、プラス記号、マイナス記号で数値の符号を指定できるようになってます。

サンプル

下のソースコードをコピー&ペーストしてコンパイルすると数式文字列の評価を確認できます。
ぜひ、試してみて下さい。

//------------------------------------------------------------------------------
// 数式評価のサンプル(実数値)
//------------------------------------------------------------------------------
#include <math.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//------------------------------------------------
// break 付きのキーワード
//------------------------------------------------
#define CASE        break;case
#define DEFAULT     break;default

//------------------------------------------------
// 関数のプロトタイプ宣言
//------------------------------------------------
static double funcAddSub();

//--[数式評価]------------------------------------------------------------------

//------------------------------------------------
// 記号定数
//------------------------------------------------
#define BLKTOP      '('
#define BLKEND      ')'
#define SPC         ' '
#define TAB         '\t'

//------------------------------------------------
// 列挙定数(エラーコード)
//------------------------------------------------
enum eERRCODE {
    ERRCODE_SUCCESS,        // 正常
    ERRCODE_FACTOR,         // 未サポートの演算子です。
    MAX_ERRCODE,
};

//------------------------------------------------
// グローバル変数
//------------------------------------------------
static const char *g_parse;
static       long  g_error;

//------------------------------------------------
// 数値文字列の評価
//------------------------------------------------
static double funcValue()
{
return strtod( g_parse, (char**)&g_parse );
} //------------------------------------------------ // 括弧・数値の評価 //------------------------------------------------ static double funcFactor() { double ans; while ( (*g_parse == SPC) || (*g_parse == TAB) ){ g_parse++; } if ( *g_parse == BLKTOP ){ g_parse++; ans = funcAddSub(); g_parse++; return ans; }
if ( isdigit(*g_parse) || (*g_parse == '+') || (*g_parse == '-') || (*g_parse == '.') ){
return funcValue(); } g_error = ERRCODE_FACTOR; return 0; } //------------------------------------------------ // 乗算・除算・剰余の評価 //------------------------------------------------ static double funcMulDiv() { double ans = funcFactor(); for ( ; ; ){ switch ( *g_parse ){ CASE '*': g_parse++; ans *= funcFactor(); CASE '/': g_parse++; ans /= funcFactor();
CASE '%': g_parse++; ans = fmod( ans, funcFactor() );
CASE SPC: g_parse++; CASE TAB: g_parse++; DEFAULT: return ans; } } } //------------------------------------------------ // 加算・減算の評価 //------------------------------------------------ static double funcAddSub() { double ans = funcMulDiv(); for ( ; ; ){ switch ( *g_parse ){ CASE '+': g_parse++; ans += funcMulDiv(); CASE '-': g_parse++; ans -= funcMulDiv(); CASE SPC: g_parse++; CASE TAB: g_parse++; DEFAULT: return ans; } } } //------------------------------------------------ // 数式文字列の評価 //------------------------------------------------ extern double calcExpress( const char string[] ) { g_parse = string; g_error = ERRCODE_SUCCESS; return funcAddSub(); } //------------------------------------------------ // 数式文字列のエラー取得 //------------------------------------------------ extern long calcErrCode() { return g_error; } //------------------------------------------------ // メイン関数 //------------------------------------------------ int main( void ) { char buff[ 256 ]; char* find; while ( fgets(buff,sizeof(buff),stdin) != NULL ){ if ( (find = strchr(buff,'\n')) != NULL ){ *find = '\0'; } if ( buff[0] != '\0' ){
printf( "%s = %f\n", buff, calcExpress(buff) );
switch ( calcErrCode() ){ CASE ERRCODE_FACTOR: printf( "ERRCODE_FACTOR\n\n" ); DEFAULT: printf( "\n" ); } } } return 0; } //------------------------------------------------------------------------------ // End of express2.cpp //------------------------------------------------------------------------------
  • コマンド・プロンプトを起動して実行すると数式入力になります。
  • そこで「((1 + 3) * 5 + 20)」とか、「10 * -3」と入力すると次のように計算されます。

実行結果

((1 + 3) * 5 + 20)
((1 + 3) * 5 + 20) = 40.000000

10 * -3
10 * -3 = -30.000000

16 * 16 + 100
16 * 16 + 100 = 356.000000

1 + 2 - 3 * 4 / 5
1 + 2 - 3 * 4 / 5 = 0.600000
  • 上記の実行結果は、ほんの一例です。
  • 特に最後の数式「1 + 2 - 3 * 4 / 5」を優先順位を考えずに計算すると「0」になります。
  • しかし、ちゃんと掛け算、割り算を先に計算してるので答えは「0.6」になってます。
  • 前回は整数値ですから「1」でしたが今回は正しく「0.6」となります。


コメント    この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« 数式文字列の四則演算(整数値) | トップ | 数式文字列の変数名と関数名 »
最新の画像もっと見る

コメントを投稿

アルゴリズム関連」カテゴリの最新記事