C++入門/浮動小数点数

柴田祐樹,東京都立大学,情報科学科
戻る

整数値の計算の節では,整数値の計算はもとより,記憶域の使い方について説明した.ここでは整数に加え,有理数,つまり小数点を含む数を扱う方法を説明する.無限の桁数が必要となる真の実数や有理数を扱えるわけではないが,だいたい小数点以下15桁ほどを近似可能となり,実用的な計算が可能となる.例えば,円周率などを求めることが可能となる.以下に例を示す.整数(int)についても同じ処理を併記してある.

#include <iostream>

int main(){
    double a;
    int b;
    a = 1.2;
    b = 1.2;
    std::cout << a << char(10);
    std::cout << b << char(10); // 小数点以下は切り捨てられる
    return 0;
}

出力は次のとおりになる.ファイル名はf.cppとする.

[shibata@127 cpp]$ g++ f.cpp [shibata@127 cpp]$ ./a.out 1.2 1

結果から,double を指定した変数 a の出力では,1.2 と意図した値が出力されていることがわかる.また,int の方は小数点以下の値が表示されないことがわかる.これはC++の決まりであり,int の変数へ小数点付きの値を代入した場合,小数点以下の数値が切り捨てられることになっている.

この int と double についていくつか変換の決まり事がある.以下にこれを列挙する.細かいがしっかり把握して置かなければプログラムが期待通りに動かない原因となるため,よく覚えてもらいたい.

以下に具体例を示す.

#include <iostream>

int main(){
    double a, b, c, d;
    int i, j, k;
    a = 1.5;
    b = 1/2;
    c = 1.0/2;
    d = (1/2) + (2.0/3);
    i = a;
    j = c/a;
    k = (1/2) + (2.0/3);
    std::cout << a << char(10);
    std::cout << b << char(10);
    std::cout << c << char(10);
    std::cout << d << char(10);
    std::cout << i << char(10);
    std::cout << j << char(10);
    std::cout << k << char(10);
    return 0;
}

コンパイルから実行までの結果は次のようになるはずである.

[shibata@127 cpp]$ g++ f.cpp [shibata@127 cpp]$ ./a.out 1.5 0 0.5 0.666667 1 0 0

実数と制御で説明した繰り返し文を使えば,Taylor 展開を用いて無理数の近似値を求めることが可能となる.例えば,以下を用いてNapier数の近似値を計算することを考える. \mathrm{e} = \lim_{n\to \infty}\sum_{k=0}^n \frac{1}{k!} この極限は計算できないため,次の形に書き換える. e_{n} = \sum_{k=0}^n \frac{1}{k!} 和の形を繰り返し分で表現可能としなければならない.これには,次の数列を考えると良い. e_{n} = \frac{1}{n!} + e_{n-1} = , e_{0} = 1, n=1, 2, ... n!についても,計算機では定義されていないため,さらに次のように数列を定義して考える. e_{n} = a_{n} + e_{n-1}, a_n = \frac{1}{n}a_{n-1}, e_{0} = 1, a_0 = 1, n=1, 2, ... これをプログラムに実装した例と実行結果を次に示す.napier.cppとして保存しコンパイルした.

#include <iostream>

int main(){
    double e_n;
    double e_n1;
    double a_n;
    double a_n1;
    int n=0;
    a_n1 = 1;
    e_n1 = 1;
    while(1 > 0){
        std::cout << n << char(32);
        std::cout << e_n1 << char(32);
        std::cout << a_n1 << char(10);
        if(n == 10){
            break;
        }
        n = n + 1;
        a_n = 1.0/n*a_n1;
        e_n = a_n + e_n1;
        a_n1 = a_n;
        e_n1 = e_n;
    }
    return 0;
}
[shibata@127 cpp]$ g++ napier.cpp [shibata@127 cpp]$ ./a.out 0 1 1 1 2 1 2 2.5 0.5 3 2.66667 0.166667 4 2.70833 0.0416667 5 2.71667 0.00833333 6 2.71806 0.00138889 7 2.71825 0.000198413 8 2.71828 2.48016e-05 9 2.71828 2.75573e-06 10 2.71828 2.75573e-07

Napier数は2.71828だと知られているが,それに近い値を確認できる.数列の計算が1項間の漸化式の計算で行える場合,このコードのように,変数を2つ用意するだけで数列を計算することができる.具体的には,18, 19行目に記載のように,次の漸化式の計算に備えて,一項前の値を示す変数を更新すれば良い.2つ以上前の数列の情報は必要ないため,このような実装が行える.この実装方法は典型的で,多くの場面で用いられるため,よく覚えてもらいたい.12, 13, 14行目のように,出力は漸化式を計算する前に配置する.このようにしておけば,初期値を確認することができるからである.繰り返しの終了判定は,while の記載場所ではなく,出力の直後においてある.このようにすることで,出力がされない無駄な計算を抑制できる.この書き方も典型である.1. 出力, 2. 終了判定. 3. 計算 の順と覚えておけば良い.

練習課題

課題1

以下の級数を計算して,nが大きくなるに従いその値が2に近づくことを確認せよ.

S = \sum_{k=0}^n \frac{1}{2^n}

課題2

以下の公式を計算するコードを実装して,nが大きくなるに従い\piが円周率の値に近づくことを確認せよ.

\pi = 4\sum_{k=0}^n \frac{(-1)^k}{2k + 1}

課題3

以下の公式を計算するコードを実装して,nが大きくなるに従い\piが円周率の値に近づくことを確認せよ.

\pi = \sum_{k=0}^n \frac{1}{16^k}\left(\frac{4}{8k+1} - \frac{2}{8k+4} - \frac{1}{8k+5} - \frac{1}{8k+6} \right)

練習課題回答

課題3: pi.cpp