+ | 加算 |
- | 減算、符号反転 |
* | 乗算 |
/ | 除算 |
% | 剰余 |
と、省略形としての +=、-=、*=、/=、%=という 演算子があると既に述べた。
int a = 10; int b = 4; double c = 10; double d = 4; printf("%d\n", a / b); printf("%g\n", c / d);の結果は、
2 2.5となる。
「%」演算子は、割ったときの余りを返す。すなわち、
int a = 10; int b = 4; printf("%d\n", a % b);の結果は、
2となる。「%」演算子は浮動小数点数には使えない。
i = i + 1;の省略形として
i += 1;と書けると既に述べた。 このような「+1」または「-1」の場合は更に省略でき、
i += 1; i -= 1;は、
i++; i--;または
++i; --i;と省略できる。
なお、この書き方は次のようにそれ自身が値を持ち式の中で 使うことが出来るが、その値は前置記法と後置記法で異なる。
a = 1; b = 1; c = a++; d = ++b; printf("%d\n", a); printf("%d\n", b); printf("%d\n", c); printf("%d\n", d);結果は、
2 2 1 2となる。前置記法は「変更後の値」、後置記法は「変更前の値」をとる。 どちらでも演算対象の変数(a、b)が+1されることに 変りは無い。すなわち、 「 c = a++; 」は、
c = a; a = a + 1;と、「d = ++b;」は、
b = b + 1; d = b;と同じ動作をする。
char | 8bit整数(文字型) |
short int | 16bit整数 |
long int | 32bit整数 |
int | 32bit整数 |
float | 32bit浮動小数点数 |
double | 64bit浮動小数点数 |
があると既に述べた。これは現在最も普及しているシステムでの 数値であり、C言語の規定では各数値のbit数には以下の 規定があるのみである。
unsigned int a; signed char b;のようにunsignedまたはsignedを 付加でき、unsignedならば0または正の数を、 signedは正負の数を表現できる。 何も付けない場合は、short int、 long ing、intの場合はsigned扱いに なる。charがどちらになるかは処理系依存である。 まず、signed int型は計算機内でどう表現されているか説明する。 int型は一般に 32bit = 4byteの記憶領域を使って記憶される。以下に、bit patternと数値との 対応を示す。
bit pattern | 数値 |
---|---|
01111111111111111111111111111111 | 2147483647 |
00000000000000000000000000000010 | 2 |
00000000000000000000000000000001 | 1 |
00000000000000000000000000000000 | 0 |
11111111111111111111111111111111 | -1 |
11111111111111111111111111111110 | -2 |
10000000000000000000000000000000 | -2147483648 |
このような負の数の表現形式を「2の補数形式」と呼ぶ。 32個の各bitが次のような重みを持っていると思えばよい。
-231 | 230 | 229 | ..... | 21 | 20 |
上記のように、2の補数形式の場合、nビットで -2n-1〜2n-1-1の範囲の数を表現できる。 すなわち、Cのsignedな整数型は、
char | -128〜127 |
short int | -32768〜32767 |
long int | -2147483648〜2147483647 |
の範囲の数が表せることになる。
次に、unsigned int型は計算機内でどう表現されているか説明する。 以下に、bit patternと数値との対応を示す。
bit pattern | 数値 |
---|---|
11111111111111111111111111111111 | 4294967295 |
00000000000000000000000000000010 | 2 |
00000000000000000000000000000001 | 1 |
00000000000000000000000000000000 | 0 |
32個の各bitが次のような重みを持っていると思えばよい。
231 | 230 | 229 | ..... | 21 | 20 |
上記のように、nビットで 0〜2n-1の範囲の数を表現できる。 すなわち、Cのunsignedな整数型は、
char | 0〜255 |
short int | 0〜65535 |
long int | 0〜4294967295 |
の範囲の数が表せることになる。
C言語では浮動小数点数の格納形式に明確な規定は無いが、 最近のCPUのほとんどはIEEE Std. 754で定められた形式で格納されている。 以下、それについて説明する。
doubleの一般的な格納形式は、符号(±)に1bit、指数部に11bit、仮数部に 52bit使って表す。全部で64bit=8byteである。
1(符号) | 11(指数部) | 52(仮数部) |
であるから、計算機内では、0.00011001100110011001100.... = 1.1001100110011001100....× 2-4
0 | 1111111011 | 1001100110011001100110011001100110011001100110011001 |
のように格納されている。指数部の「1111111011」は、「-4+1023=1019」 を2進数にしたものである。
有効数字は、2進数で53桁である。10進数に直すと、
log10(253) ≒ 15.95より約16桁であると考えられる。
ついでに、floatは、全部で32bit=4byteで、
1(符号) | 8(指数部) | 23(仮数部) |
のようになっている。 有効数字は、2進数で24桁、10進数に直すと、
log10(224) ≒ 7.22より約7桁。
例えば、代入の場合、
double d; int a; a = 2; d = 3.14; d = a; a = d;のように、異なった型の変数を代入すると、 適当に変換されてから(自動型変換という)代入される。この場合、
double d; int a; a = 2; d = 3.14; a = (int)d; d = (double)a;と書くことも出来る。異なった型同士の代入の場合は、思わぬ自動型変換 によるバグを防ぐためにも、キャストを用いて書いた方が良い。
次に、2項演算子の両辺に異なった型が用いられた場合を考える。
double d; int a; a = 2; d = 3.14; printf((double)a * d); printf(a * (int)d); printf(a * d);この実行結果は、
6.28 6 6.28となる。一般に異なった型同士の演算を行った場合は、このようにより 表せる数が多い方の型(double)に他方(int)が自動的に 変換されてから計算される。
a = 2; a = a + 0.3;の「2」や「0.3」ようなプログラム中の定数は、「小数点が 含まれていなければint型、含まれていればdouble型」と 解釈される。異なる型どうしの代入、演算については、 前節のように普通は自動的に変換されるので問題は起きないが、 ときとして予想外な問題を引き起こす。 例えば、数値を0〜1.0まで0.1刻みで変化させてそのsinを表示しようとして、
int i; for (i=0; i<=10; i++) { printf("%g\n", sin(i / 10)); }と書くと、「i」も「10」もint型なので、 整数型の割算(小数点以下切捨て)が 行われ、期待通りの結果にならない。 「i / 10」が「i / 10.」と小数点付きで書かれていれば、 この問題は起こらない。
結論として、プログラム中に実数の定数を書くときは、必ず小数点を付ける 習慣を付けると良い。
なお、「10-15」や、「6.02×1023」などの 非常に大きい(小さい数)をプログラム中に書くときは、 「1e-15」や、「6.02e-23」 などの「e表記」が使える
現在の計算機のほとんどは、既にfloatとdoubleの 計算速度は違いが無くなっているので、計算速度を稼ぐ目的で floatを使用しても意味は無い。 ただ、記憶に使う領域の大きさは違うので、メモリを大量に用いる 計算でfloatを用いる意味はある。
また、実数は2進数で格納されるので、10進数の世界に 慣れている我々にとっては思わぬ現象が起きることがある。 上の「(1./3.)*3.」が1にならないのは普通の電卓なんかでも 観察できるのでそれほど違和感を持たないと思う。 ところが、例えば「1./10.」は、
double x; x = 0.; while (1) { printf("%g\n", x); if (x == 1.) break; x = x + 0.1; }