ホーム » 2018 » 4月 » 20

日別アーカイブ: 2018年4月20日

2018年4月
1234567
891011121314
15161718192021
22232425262728
2930  

検索・リンク

実数の注意点

C言語でプログラムを作成していて、簡単な数値計算のプログラムでも動かないと悩んだことはないだろうか?解らなくて友達のプログラムを真似したら動いたけど、なぜ自分のプログラムは動かなかったのか深く考えたことはあるだろうか?

まずは動く例

以下のプログラムは、見れば判るけど、th を 0度〜360度まで5度刻みで変化させながら、y = sin(th) の値を表示するプログラム。

// sin の値を出力
#include <stdio.h>
#include <math.h>

int main() {
    double th , y ;
    for( th = 0.0 ; th <= 360.0 ; th += 5.0 ) {
        y = sin( th / 180.0 * 3.1415926535 ) ;
        printf( "%lf %lf¥n" , th , y ) ;
    }
    return 0 ;
}

動かないプログラム

では、以下のプログラムはどうだろうか?

// case-1 ---- プログラムが止まらない
#define PI 3.1415926535
int main() {
    double th , y ;
    // 0〜πまで100分割でsinを求める
    for( th = 0.0 ; th != PI ; th += PI / 100.0 ) {
        y = sin( th ) ;
        printf( "%lf %lf¥n" , th , y ) ;
    }
    return 0 ;
}
// case-2 ---- y の値が全てゼロ
int main() {
    int    th ;
    double y ;
    for( th = 0 ; th <= 360 ; th += 5 ) {
        y = sin( th / 180 * 3.1415926535 ) ;
        printf( "%d %lf¥n" , th , y ) ;
    }
    return 0 ;
}

どちらも、何気なく読んでいると、動かない理由が判らないと思う。そして、元のプログラムと見比べながら、case-1 では、「!=」を「<=」に書き換えたり、case-2 では、「int th ;」を「double th ;」に書き換えたら動き出す。

では何が悪かったのか…
回答編

数値の範囲に注意

前節の回答編で示したが、数値の扱える値の範囲に注意すべきである。
私自身が自分で書いたプログラムで悩んだ例を以下に示す。

// 16bit コンピュータの時代に、
//   画面上のマウスとターゲットの距離を計算したかった
int distance( int x0 , int y0 , int x1 , int y1 ) {
    int dx = x1 - x0 ;
    int dy = y1 - y0 ;
    return sqrt( dx * dx + dy * dy ) ;
}

このプログラムを実行した時、通常はうまく動くのだけど、時々「sqrt は、負の値では計算できません」というエラーを表示する。

なぜだろうか?
回答編

2進数への変換(補助資料)

10進数で 123.45 は、1*102 + 2*101 + 3*100 + 4*10-1 + 5*10-2 を意味する。(あたりまえ)

2進数に変換する場合、整数部と小数部に分けて考えると、

10進数なら、それぞれを 10 で割る、10 をかけると
  123 / 10 = 12.3 小数部に3 が出てくる。
  0.45 * 10 = 4.5 整数部に4 が出てくる。

2進数なら、それぞれを 2 で割る、2をかけると...
123.45 を 2進数に変換

 2)123 )10 = 1111011 )2
    ̄ ̄ ̄ ̄
 2) 61 ... 1    次々と2で割って、余りを求める
    ̄ ̄ ̄ ̄
 2) 30 ... 1
    ̄ ̄ ̄ ̄
 2) 15 ... 0
    ̄ ̄ ̄ ̄
 2)  7 ... 1
    ̄ ̄ ̄ ̄
 2)  3 ... 1
    ̄ ̄ ̄ ̄
 2)  1 ... 1
    ̄ ̄ ̄ ̄
     0 ... 1

✕2)0.45 )10 = 0.01110011001100...)2
    ̄ ̄ ̄ ̄ ̄
✕2)0.9 ... 0    次々と2倍して、整数部を求める
    ̄ ̄ ̄ ̄ ̄
✕2)1.8  ... 1 ※
    ̄ ̄ ̄ ̄ ̄
✕2)1.6 ... 1
    ̄ ̄ ̄ ̄ ̄
✕2)1.2 ... 1
    ̄ ̄ ̄ ̄ ̄
✕2)0.4 ... 0
    ̄ ̄ ̄ ̄ ̄
✕2)0.8 ... 0 ※の繰り返し
    ̄ ̄ ̄ ̄ ̄
✕2)1.6 ... 1
    ̄ ̄ ̄ ̄ ̄
   :
よって、123.45 )10 = 1111011 .011100110011...)2

大域変数・局所変数・スコープ

繰り返しが動かない例

#include <stdio.h>
int i ;
void foo() { // foo() は 3個Aを表示するプログラム。
   for( i = 0 ; i < 3 ; i++ )
      printf( "A" ) ;
}
int main() {
   foo() ;
   return 0 ;
}

では、

#include <stdio.h>
int i ;
void foo() { // foo() は 3個Aを表示するプログラム。
   for( i = 0 ; i < 3 ; i++ )
      printf( "A" ) ;
}
int main() {
   // A はいくつ表示される?
   for( i = 0 ; i < 3 ; i++ )
      foo() ;
   return 0 ;
}

大域変数と局所変数

編集中:もう少し加筆予定

システム

最新の投稿(電子情報)

アーカイブ

カテゴリー