ホーム » スタッフ » 斉藤徹 » 講義録 » 情報制御基礎 » プログラムと数値型

2019年4月
« 3月   5月 »
 123456
78910111213
14151617181920
21222324252627
282930  

最近の投稿(電子情報)

アーカイブ

カテゴリー

プログラムと数値型

コンピュータの中では、2進数で値を表現するが、組み込み系のような小さいコンピュータでは、たくさんの桁を必要とする情報を扱うことが苦手である。そこで、C言語で数値を扱う型と、その型で扱える数値の範囲や問題点を説明する。

補足資料:プログラミングの基礎として、C言語の基礎を示す。

数値を扱う型

C言語では、データを覚える型を大きく2つに分けると、整数型(int)実数型(float)に分けられる。

整数型(int)

整数型も正の値しか覚えられない符号無し型(unsigned int)と、符号付き型(signed int)に分けられる。さらに、その値を8bitで覚える文字型(char)、16bitで覚える short int型、32bitで覚える int 型、64bitで覚える long int 型(※C言語では long int で宣言すると32bitの場合も多いので要注意)がある。

精度 符号あり 符号なし
8bit char unsigned char
16bit short int unsigned short int
32bit int unsigned int
64bit long int※ unsigned long int※

符号付きのデータは、負の数は2の補数によって保存する。この場合2進数の最上位bitは、負の数であれば必ず1となる。

整数型で扱える数

例えば、2進数3桁であれば、000,001,010,011,100,101,110,111 で、10進数であれば 0~7 の8通りの値が扱える。

(例) 符号なしの1byte(8bit)であれば、いくつの数を扱えるであろうか?

一般的に N bit であれば、0(2N-1) までの値が扱える。

bit数 符号なし
8 unsigned char 0~28-1 0~255
16 unsigned short int 0~216-1 0~65535
32 unsigned int 0~232-1 0~4294967295

符号付きであれば、2の補数表現で最上位bitが0であれば正の数、1であれば負の数を表す。このため、N bit の符号つき整数は、-2N-1から2N-1-1の範囲の値を覚えられる。

bit数 符号あり
8 char -27~27-1 -128~127
16 short int -215~215-1 -32768~32767
32 int -231~231-1 -2147483648~2147483647

数値の範囲の問題で動かないプログラム

この話だけだと、扱える数値の上限について実感がわかないかもしれないので、以下のプログラムをみてみよう。
組み込み系のコンピュータでは、int 型でも、一度に計算できるbit数が少ない。例えば、int型が16bitコンピュータでは、以下のプログラムは期待した値が計算できない。以下の例では、16bit int型として short int で示す。

// コード1
#include <stdio.h>
#include <math.h>

int main() { // 原点から座標(x,y)までの距離を求める
   short int x  = 200 ;
   short int y  = 200 ;
   short int r2 = x*x + y*y ;  // (x,y)までの距離の2乗
   short int r  = sqrt( r2 ) ; // sqrt() 平方根
   printf( "%d\n" , r ) ;      // 何が求まるか?
   return 0 ;                  // (例) 282ではなく、120が表示された。
}

コンピュータで一定時間かかる処理を考えてみる。

// コード2.1
// 1 [msec] かかる処理が以下のように書いてあったとする。
short int i ;
for( i = 0 ; i < 1000 ; i++ )
   NOP() ; // NOP() = 約1μsecかかる処理とする。

// コード2.2
// 0.5 [sec]かかる処理を以下のようにかいた。
short int i ;
for( i = 0 ; i < 500000 ; i++ )
   NOP() ;
// でもこの処理は16bitコンピュータでは、1μsecもかからずに終了する。なぜか?

上記の例は、性能の低い16bit コンピュータの問題で、最近は32bit 整数型のコンピュータが普通だし、特に問題ないと思うかもしれない。でも、32bit でも扱える数の範囲で動かなくなるプログラムを示す。

OS(unix) では、1970年1月1日からの経過秒数で時間を扱う。ここで、以下のプログラムは、正しい値が計算できない有名な例である。(2004年1月11日にATMが動かなくなるトラブルの原因だった)

// コード3.1
int t1 = 1554735600 ; // 2019年4月09日,00:00
int t2 = 1555340400 ; // 2019年4月16日,00:00

// この2日の真ん中の日を求める。
//  以下のプログラムは、正しい 2019年4月12日12:00 が求まらない。なぜか?
int t_mid = (t1 + t2) / 2;  // (例) 1951年03月25日 08:45 になった。

// コード3.2
//  以下のプログラムは正しく動く。
//   time_t 型(時間処理用の64bit整数)
time_t t1 = 1554735600 ; // 2019年4月09日,00:00
time_t t2 = 1555340400 ; // 2019年4月16日,00:00

// たとえ32bitでも溢れない式
time_t t_mid = t1 + (t2 - t1) / 2 ;