変数には型がありますね。同じ変数の型同士の代入なら、意図しない結果になることはありません。
しかし、異なる変数の型の代入、同じ型同士でも演算結果を代入する場合は、意図しない結果になることがあります。
それは、変数の型が扱える数値の範囲が異なるからです。
例えば、short型同士の変数の掛け算をし、short型の変数に代入しようとした場合、short型では扱えない数値の演算結果になってしまう可能性があることがわかりますか?
ちなみに、short型の扱える数値の範囲は-32,768~32,767です。
short型で100*200なら、演算結果は20,000なので、short型で扱えます。
しかし、30000*100なら、演算結果は3,000,000となり、short型で扱える範囲を超えてしまいます。
このようにならないためには、演算結果を格納する変数の型をlong型にしておく必要があります。そうすることで、short型の変数同士の掛け算を問題なく扱えるようになります。
ここでは、変数の型変換について、遊び感覚で、身につけられるように書いていきます。
型変換とは
型変換とは、定義した型とは異なる別の型に変えることです。
型変換をしても、定義した変数の型が変わるのではなく、演算や代入が行われるときだけ、型が変わります。
C言語では、特に変換する型を記述しなくても、暗黙の型変換が行われます。
代入の時は、左辺の変数の型に、演算では基本的には精度の高い型へ変換されます。
double > float > long > int > short > char
の順で型変換が行われて、演算がなされます。
この順で型変換をしたくないときは、明示的に型変換したい型を記載します。
例えば、long a という宣言があって、変数aをshort型に型変換したいときは、「(short)a」と記述します。
ただし、long型の変数をshort型にするので、上位の桁が削られるので、意図しない結果にならないように注意する必要があります。
代入時の型変換
代入時の型変換がどういう結果になるか、以下のプログラムを作って、遊んでみましょう。
「original」に浮動小数点型のデータを入れたら、各変数の型に型変換されて、代入され、その結果を出力するプログラムです。
#include <stdio.h> void main(void) { double original; char a; short b; long c; float x; double y; printf("original = "); scanf("%lf", &original); a = original; b = original; c = original; x = original; y = original; printf("a = %d", a); printf("a = %d", b); printf("a = %d", c); printf("x = %f", x); printf("y = %lf", y); }
例えば、original に 32767.4367 と入力したら、結果は次のようになると思います。
a = -1
b = 32767
c = 32767
x = 32767.4367
y = 32767.4367
char、short、longは整数型ですので、小数点以下は切り捨てられます。さらに、charは-128~127までしか扱えないので、-1となってしまいます。
もうひとつやってみます。今度は、original に 123456 と入力します。
結果は
a = 64
b = -7616
c = 123456
x = 123456.0
y = 123456.0
となるでしょう。今度は、short型の範囲である-32768~32767を超えてしまっているので、上位の桁は削られてしまって、全然違う値になってしまっているのがわかりますね。
また、整数値ですが、変数x, yの場合は、「.0」が表示されます。
符号ありでやりましたが、unsignedをつけて符号なしでも、やってみてください。また、違った結果になると思います。
入力した値と違う結果になる可能性があるということが、おわかりいただけたかと思います。
演算時の型変換
今度は演算時の型変換をし、結果がどういう風になるかを見ていきましょう。
演算時の型変換の優先順位は double > float > long > int > short > char とお話ししました。
なので、基本的には一番制度のいい型で演算されます。
問題は、演算結果を代入する変数の型です。こちらのほうを意識しておかないと、せっかく精度よく計算しても、変数の型のサイズが小さいために、数値が切り捨てられたり、上位の桁が削られたら、意味がありませんね。
#include <stdio.h> void main(void) { short result[2]; short a; double b; a = 320; b = 505.8; result[0] = a * b; result[1] = a / b; printf("掛算 = %d", result[0]); printf("割算 = %d", result[1]); }
この場合、掛算も、割算も期待した結果にならないでしょう。演算自体はdouble型で行われます。なので、演算されたときは、きちんと期待した値になっているのですが、結果を格納するときに、short型に型変換されてしまうため、上位桁の削除、小数点の切捨てが発生します。
したがって、演算結果の型を意識して、格納する変数の型を決めるようにしましょう。
次に、以下のプログラムを見てください。
#include <stdio.h> void main(void) { double result; short a; short b; a = 320; b = 505; result = a / b; printf("result = %d", result); result = (double)(a / b); printf("result = %d", result); result = (double)a / (double)b; printf("result = %d", result); }
このとき、正しくdouble型の結果を出力できるのは、どれだかわかりますか?
答えは、3番目の型変換の場合のみ、正しくdouble型で表示されます。
1番始めの式はshort型同士の演算なので、演算結果も当然short型になります。したがって、結果は0となります。
2番目の式はdouble型に変換しているので、一見、double型の演算結果になるように見えますが、残念ながら、short型の演算した結果をdouble型に変換しているだけなので、結果は0となります。
3番目の式は変数a,bをそれぞれ、double型に変換してから、割算を行っているため、double型の結果になります。このときの答えは、0.633633633…となります。
演算結果を正しく出すためには、演算する際の型も重要になります。いまは、割算で例を示しましたが、足算、引算、掛算でも同じです。short型で足した結果がshort型の範囲を超える可能性もあるわけです。なので、longに型変換して演算すると安心ですね。
ここで、疑問が浮かぶかもしれません。じゃあ、long同士の演算はどうするんだと。
これの回答としては、long同士の演算をさせるときに、各変数がプログラム内で取りうる最大や最小の数値がいくつかを考え、その結果、longの型に収まるのであれば、そのまま演算させればいいでしょう。
もし、演算結果がlongの値を超えてしまうのなら、シフト演算を用いて、longの型に収まるように変数の精度を落として演算し、シフト量も記憶させておきます。その演算結果の値を用いるとき、シフト量も活用します。
組み込みマイコンでは、浮動小数点を扱いませんから、精度を保つために、シフト演算を用いて、演算を行っています。
まとめ
型変換について、解説しました。複雑なことはなかったと思いますが、いかがでしたか?
代入時に型は左辺の型になる、演算時は精度のよい型になると覚えておけばいいでしょう。
演算した結果の精度を保ちたければ、代入する変数の方を精度のよいものにしましょう。
組み込みマイコンでは、メモリが限られているため、簡単に型を精度のよいものにできないかもしれません。その場合は、シフト演算をうまく利用して、とんでもない演算結果にならない工夫をしましょう。
最低限これだけ知っておけば、変数を宣言するとき、最適な型をつけることができるでしょう。