Unix C プログラミング入門資料(高崎金久)   4.実数演算,数学函数,数値データの入出力 目次 1.今回のテーマについて 2.プログラムの例 3.プログラムの解説 4.文字列->数値変換函数を使う入力方法 -------------------------------------------------------------------------- 1.今回のテーマについて 1)実数(浮動小数点数)の特徴 今回は実数(浮動小数点数)の取り扱いについて考えます.計算機では数学的な意 味での実数(小数点以下に無限個の桁をもつ)を直接に扱うことはできません.そ の代わりに用いられるのが一定の有効桁数をもつ「基数部」と小数点の位置を表わ す「指数部」の組み合わせからなる浮動小数点数です. 計算機内部では浮動小数点数は整数とはまったく異なる形式で表現されています. プログラミング言語はその違いをある程度吸収するように設計されています.例え ばC言語では浮動小数点数を10進展開の形で 31415.9265, 3.14159265e+4, .314149265e+5 などと書くことができます.(後の2つは10進展開の意味での指数表示で,e+4, e+5 はそれぞれ10の4乗,10の5乗を意味しますから,これらは実はすべて同 じ数です.)しかし整数型データと浮動小数点数型データが入り交じるプログラム では両者の違いが露呈します.例えば,m,n を整数型変数,x を浮動小数点型変数 とするとき x = m / n; /* たぶん意図とは違う動作をする */ という代入文は m / n を整数の意味で(つまり割り算の余りを切り捨てて)計算 して結果を x に代入するという指示と解釈されます.実数の意味の割り算をさせ るつもりでこのように書くと,間違った動作をするプログラムができます(これは よくあるミスです).実数の意味の割り算をさせるにはいろいろやり方があります が,例えば x = 1. * m / n; /* たぶん意図通りの動作をする */ と書きます.「1.」は浮動小数点数としての1で,数学的には整数の1と同じです が,計算機の内部ではまったく異なるデータです.上の文の右辺ではまずこの 1. と整数 m の浮動小数点数としての掛け算が行われ,そのあと 整数 n による割り 算がやはり浮動小数点数として行われます.結果として右辺が浮動小数点数として 計算されて左辺に代入される,というわけです.なお,型変換(キャスト)演算子 と呼ばれるものを用いれば,同じことを(「double」型への型変換によって) x = (double)m / n; と書くこともできます. C言語では2種類の浮動小数点数の型(float, double)が標準的に用意されてい ます.float は単精度型,double は倍精度型と呼ばれ,それぞれ10進数の意味 では6ー7桁および14桁程度の精度があります.応用上は単精度では不十分なこ とが多く,メモリーを節約しなければならない場合を除けば,普通は倍精度を使い ます.倍精度でも足りない場合もありますが,その場合は多倍精度浮動小数点数を 使える処理系を使うか,自分で特別なプログラムを行う必要があります. 2)数学函数ライブラリ C言語の処理系では以下のような数学函数が標準ライブラリとして提供されていま す(いずれも引数・返戻値ともに double). ★三角函数 sin(x), cos(x), tan(x) ★逆三角函数 asin(x), acos(x), atan(x) ★双曲線函数 sinh(x), cosh(x), tanh(x) ★指数・対数函数 exp(x), log(x), log10(x) ★巾函数 pow(x, y) /* x の y 乗 */ ★平方根 sqrt(x) ★天井 (ceiling) ceil(x) /* x 以上の整数の最小値 */ ★床 (floor) floor(x) /* x 以下の整数の最大値 */ ★絶対値 fabs(x) *使用上の注意* これらを使うにはプログラムの先頭に #include という命令を付け加える必要があります.また,コンパイルするときにも gcc program.c -lm ... というように「-lm」というオプションを付けてコンパイラを呼び出さなければなり ません. 2.プログラムの例 今回は典型的な浮動小数点数計算の例として次の問題を考えます. *問題*  正実数を入力し,ニュートン法によって平方根の近似値を計算して, 結果を表示するプログラムをつくること. ニュートン法というのは方程式 f(x) = 0 の根の近似値を求めるアルゴリズムで, 適当な初期値 x(0) から出発して x(n+1) = x(n) - f(x(n))/f'(x(n)) (f'(x) は f(x) の導函数)という漸化式で決まる数列 x(n) の n->∞ での極限 値として方程式の(ひとつの)根を得る方法です.これは while 文による繰り返 しとして簡単に実現できます.もちろん無限に x(n) を計算して極限値を求めるこ とはできませんから,適当な条件(収束条件)が満たされたところで繰り返しをや めて,その時点での x(n) の値を近似値として採用することになります.結局アル ゴリズムは次のようになります. *ニュートン法のアルゴリズム* ●変数 x を適当な初期値に選ぶ. ● xx = x - f(x) / f'(x) ■ 収束条件が満たされない限り繰り返し ● x = xx ● xx = x -f(x) / f'(x) ● x を近似値として採用する 正実数 a の平方根を求めるときには f(x) = x*x - a としてニュートン法を適用 すればよいわけです.ニュートン法を使う際には初期値の選び方がいつも問題にな りますが,今の場合は(f(x) が下に凸な函数なので)それほど神経質になる必要 はありません. プログラムの例を以下にしめします. 1:/************************************************************** 2: 与えられた正数にニュートン法で平方根の近似値を計算し, その値, 3: ニュートン法の反復回数, および 残差を表示する 4: **************************************************************/ 5: 6:#include 7:#include 8: 9:int main(void) 10:{ 11: double a, x, eps = 0.000001; /* eps は収束パラメータ */ 12: int n; 13: printf("正実数の平方根を求めます.\n"); 14: printf("正実数を入力して下さい:"); scanf("%lf", &a); 15: if (a < 0){ 16: printf("それは正実数ではありません.\n"); exit(1); 17: } 18: x = (a + 1.)*0.5; n = 1; 19: while(fabs(x*x - a) > eps){ 20: x = (x + a/x)*0.5; n = n +1; 21: } 22: printf("平方根の近似値 %.10lf, 反復回数 %d, 残差 %.10lf\n", x,n,x*x-a); 23: return 0; 23:} すでに注意したように,このプログラムをコンパイルするときには「-lm」という オプションを付けなければなりません. 3.プログラムの解説 6行ー7行:例によって #include 命令が来ますが,今回は数学函数を呼び出すた め「#include 」が加わっているのが特徴です. 14行目:ここで浮動小数点数データを scanf で読み込んで変数 a に格納してい ます.このような場合は &a というようにアドレス演算子が必要です.double型デ ータを読み込む(あるいは書き出す)ときには「%lf」という変換仕様を使います. 15行ー17行:読み込んだのが負の数ならばエラーメッセージを表示してプログ ラムを終了します(終了コード1). 18行ー21行:これがニュートン法の本体です.18行目は x(0) = a として第 1回目の反復を行うものです.fabs は絶対値を求める函数で,収束条件を書くた めに用いられています.ここでは,x*x - a(このような量を残差といいます)が eps 以下になったところで反復をやめ,その時の x の値を求める平方根の近似値 とします.n はこの反復の回数を数えるための変数です. 22行目:計算した近似値などを表示します.double 型データの表示の変換仕様 は入力の時と本質的に同じ「%lf」ですが,小数点以下10桁まで表示するために 「%.10lf」という形で使っています.(なお,実際には「%lf」の代わりに「%f」 でも構わない.)浮動小数点数の表示のための変換しようには他に指数表示,10 進と指数の適当な方を選ぶ表示,などがあります. *浮動小数点数の変換仕様* float 用 %f -- 10進表示 %e -- 指数表示 %g -- 10進または指数 double用 %lf -- 10進表示 %le -- 指数表示 %lg -- 10進または指数 出力のときには実は float 用ですべて通していいのですが,入力の場合は float 用と double 用を正しく使い分けないと正しく動作しません. *桁数指定のパラメータ* 例:%10.4lf -- 全体が10桁右詰め,小数点以下は4桁の10進表示 同様の桁数パラメータは整数の変換仕様でも使えます.詳しくは参考書やオンライ ンマニュアル「man printf」「man scanf」を見てください. 4.文字列->数値変換函数を使う入力方法 文字列入力函数と数値への変換函数を組み合わせて浮動小数点数を入力することも できます.浮動小数点数への変換には atof という函数を使います. *文字列->浮動小数点数変換函数 atof(string) --- 文字列を double 型に変換してその値を返す これを用いて上のプログラムの最初の方(6行ー14行)を書き直すと次のように なります. #include #include #include int main(void) { double a, x, eps = 0.000001; /* eps は収束パラメータ */ int n; char buffer[40]; printf("正実数の平方根を求めます.\n"); printf("正実数を入力して下さい:"); fgets(buffer,40,stdin); a = atof(buffer); (以下は同じ) *変更点 1.「#include 」という行.これは変換函数を使うためのもの. 2.文字列入力用に「buffer」という変数を用意した. 3.最後の行で文字列の入力と数値データへの変換を行なっている.