C言語と数値に関する処理

数値処理

C言語には、数値、および数学を扱うために必要な処理が多数存在します。実用的なプログラムを作る上で、これらは欠かせません。そこで、ここでは数値・数学に関する 処理および関数、更には注意点などについて説明していきます。

乱数

サンプルプログラム

乱数とはでたらめな数のことです。ゲームなどでさいころを振ったり、カードをシャッフルするなどといった処理を行うには、乱数の処理が欠かせません。 ここでは、C言語で乱数を扱う方法を紹介していきます。

listex1-1:main.c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void main(){
  int a,b;
  // 乱数の初期化
  srand((unsigned) time(NULL));
  //	1から10までの乱数を発生させる
  a = rand() % 10 + 1;
  b = rand() % 10 + 1;
  //	計算結果を出力
  printf("%d + %d = %d¥n",a,b,a+b);
}
実行結果(実行する度に変化する)
5 + 3 = 8

乱数関連の関数

このプログラムの特徴は、実行するたびに結果が変わることです。それは、10行目、11行目で変数a,bに値を代入する際に使用するrand()関数に理由があります。 まずは、このプログラムで用いられている関数を紹介しましょう。(表1-1)

表1-1.乱数に関する関数
関数書式意味使用例
srand()srand(unsigned seed);randで発生させる乱数の系列を変更。srand((unsigned) time(NULL));
rand()rand()0~RAND_MAX の間の疑似乱数を返す。
(RAND_MAXは処理系依存)
rand() % 10 + 1;

time関数

これだけを見ると、srand()を使用する意味がよくわからないと思います。実は、C言語で発生させる乱数は、擬似的なもので、この処理を行わないと、 なんども同じパターンの乱数が発生してしまいます。

そこで、srand()関数を用いて変数を初期化するのですが、この時、引数が同じでは、やはり同じパターンの数値しか出ません。そこで、 引数として、秒単位で現在時刻を返すtime()関数を用い、実行するごとに違う系列の乱数が出現するようにしているわけです。

表1-2.time関数
関数書式意味使用例
time()time_t time(time_t *timer);1970年 1月 1日の00:00:00 から現在までの
経過時間を秒単位で取得する。
time(NULL);

なお、rand(),srand()関数を利用するには、stdlib.h、time()関数を利用するには、time.hをインクルードする必要があります。

乱数の範囲を指定する

では、プログラムの中身を紹介していきましょう。まず、8行目で乱数を初期化しています。次に、10,11行目で乱数を発生させていますが、 通常、rand()関数は、0からRAND_MAXという非常に大きな値を発生させます。そのため、1から10の乱数を発生させるには、まず%10をします。 これは10で割った余りという意味なので、乱数は0から9の範囲に限定されますから、それに1を足せば、1から10の値を得ることが出来るのです。(図1-1)

図1-1.1から10までの乱数を発生させる原理
1から10までの乱数を発生させる原理

考え方としては難しいですが、以下の方法を覚えておくと、非常に便利でしょう。

1からnまでの乱数をえる方法

rand() % n + 1

0からnまでの乱数をえる方法

rand() % (n + 1)

数学関数

サンプルプログラム

C言語には、沢山の数値計算用の関数が用意されています。非常にたくさんあり、全てを紹介することはできませんが、比較的使用頻度の高いものをここでいくつか紹介していきましょう。

まずは、三角関数を使ったサンプルを見てみましょう。

listex1-2:main.c
#include <stdio.h>
#include <math.h>

#define PI 3.14

void main(){
	int angle;
	double rad;
	printf("角度を入力してください(0~360):");
	scanf("%d",&angle);
	//	角度をラジアンに変換
	rad = PI * (double)angle / 180.0;
	//	三角関数での計算
	printf("sin(%d)=%f¥n",angle,sin(rad));
	printf("cos(%d)=%f¥n",angle,cos(rad));
	printf("tan(%d)=%f¥n",angle,tan(rad));
}
実行結果①(入力した値が同じ場合)
角度を入力してください(0~360):45
sin(45)=0.706825
cos(45)=0.707388
tan(45)=0.999204

三角関数

sin、cos、tanは、その綴りのとおり、ずばりサイン・コサイン・タンジェントを意味します。つまり、数学でいうところの三角関数です(表1-3.参照)。

表1-3.C言語で用いられる三角関数
関数書式意味使用例
tan()tan(double rad);()に指定したラジアンを入れるとタンジェントが得られる。doube a = tan(2*3.14);
sin()sin(double rad);()に指定したラジアンを入れるとサインが得られる。doube a = sin(2*3.14);
cos()cos(double rad);()に指定したラジアンを入れるとコサインが得られる。doube a = cos(2*3.14);

なお、これらはすべて、math.hに含まれています。

ラジアン

ただ、引数として与えるのは、角度ではなく、ラジアンだという点に注意してください。通常、角度を0度から360度までで測るやり方を、度数法(どすうほう)と言いますが、これに対し、ラジアンで角度を指定する方法を弧度法(こどほう)と言います。

ラジアンという考え方は、180度をπ(パイ)ラジアンと考えるやり方で、一般の角度をラジアンに変換するには、180で割り、そこに円周率をかけてやる必要があります。

定数の設定

この計算は、12行目で行っていますが、円周率を表すPIが定義されているのは4行目です。#defineマクロはすでにファイル分割で扱いましたが、このように、定数を定義するのにも用いることが出来ます。

#defineマクロによる定数の定義
#define PI 3.14

このようにすれば、PIという文字列が出てくれば、このソースの中では、3.14として扱うことが出来ます。

マクロには、定数を表現する方法のほかに、引数を与えて関数のような処理を行う引数付きマクロと呼ばれるものがあります。詳しくは、こちらをご覧ください。

その他の数学関数

続いては、その他よく使う数学関数を見てみましょう。

listex1-3:main.c
#include <stdio.h>
#include <math.h>

void main(){
	int n = -2;
	double d1 = -2.5,d2 = 4.0;
	printf("%dの絶対値は%d¥n",n,abs(n));
	printf("%fの絶対値は%f¥n",d1,fabs(d1));
	printf("%fの2乗は%fです。¥n",d2,pow(d2,2));
	printf("%fの平方根は%fです。¥n",d2,sqrt(d2));
}

実行結果
-2の絶対値は2
-2.500000の絶対値は2.500000
4.000000の2乗は16.000000です。
4.000000の平方根は2.000000です。

使用した関数の説明をしましょう(表1-4)。

表1-4.主な数学関数
関数書式意味使用例
abs()abs(int n);与えられた整数の絶対値を求める。int n = abs(-10);
fabs()fabs(double d);与えられた実数の絶対値を求める。double d = fabs(-3.1);
pow()pow(double x,double y);xのy乗を求める。double d = pow(3.0,2.0);
sqrt()sqrt(double d);与えらられた実数の平方根を求める。double d = sqrt(25.0);

絶対値を求める関数には、abs(),fabs()の二種類があります。状況に応じて使い分けましょう。

ビット演算

サンプルプログラム

今まで数値の演算ばかり扱ってきましたが、ビット演算も大事な演算です。以下にC言語におけるビット演算のサンプルを取り上げてみました。実行してみてください。

listex1-4:main.c
#include <stdio.h>
#include <stdlib.h>

void main(){
	//	16進数
	unsigned char i = 0xf;	//	2進数:00001111
	unsigned char j = 0xff;	//	2進数:11111111
	printf("%x << 1 = %x¥n",i,i << 1);			//	1ビット左シフト: 2進数:00011110 = 0x1e0
	printf("%x >> 1 = %x¥n",i,i >> 1);			//	1ビット右シフト: 2進数:00000111 = 0x7
	printf("%x | %x = %x¥n",i,j,i | j);			//	OR演算  : 2進数:11111111 = 0xff
	printf("%x & %x = %x¥n",i,j,i & j);			//	AND演算 : 2進数:00001111 = 0xf
	printf("~%x = %x¥n",i,(unsigned char)~i); 	//	NOT演算 : 2進数:11110000 = 0xf0
}
実行結果
f << 1 = 1e
f >> 1 = 7
f | ff = ff
f & ff = f
~f = f0

ビット演算の演算子

以下、演算子とその処理の内容を表にまとめます(表1-5)。

表1-5.ビット演算に用いる演算子
演算子意味使用例
<<左シフト。指定されたビット数分、数値を左シフトする。00001111 << 1 → 00011110 (1ビット左シフト)
>>右シフト。指定されたビット数分、数値を右シフトする。00001111 >> 1 → 00000111(1ビット右シフト)
|論理和(OR演算)。二つの数値の論理和をとる。00001111 | 11111111 → 11111111
&論理積(AND演算)。二つの数値の論理積をとる。00001111 & 11111111 → 00001111
~否定演算(NOT演算)。入力された1なら0に、0なら1に反転。~00001111 → 11110000

これらの演算は、いわゆる数学的な処理とは違いますが、様々な場面で応用されることがあります。基本的な使い方をおさえておきましょう。