『Cプログラミング診断室』目次次(第4章 キャストが好き float型対double型)

第4章 キャストが好き

自動的型変換


では、今回のプログラムの問題点をチェックしていきましょう。まず目につくのが、キャストの 多さです。(float)とか、(double)とか、(long)などと、かっこの中に「型」だけが書かれている ものです。C言語では、この(float)などという、型だけをかっこでくくったものも演算子として 扱われます。C言語では、キャストによる型変換は必要不可欠ですが、キャストをリスト全体にいっ ぱいばらまかれてしまうと、非常に読み辛くなります。

■代入時の自動的型変換■

C言語では、演算あるいは代入などにおいて、自動的型変換(暗黙的型変換)が行なわれます。 オリジナルでは、代入のとき次のようにキャストしています。

    60      w1 = (double)a;
    95      *cts = (float)1.0;
   113      *xx = (long)xd;
  
60行は、float型のaをdouble型のw1に代入するために、(double)a によりaをw1と同じdouble型 に変換し、それから代入しています。

95行は、定数値の1.0を float *cts に代入するために、わざわざfloat型に変換しています。

113行は、float型のxdを、long *xx に代入するために、整数型が浮動小数点型に代入できない と思い、キャストしているのでしょう。

上の3例はキャストは不要で、単純に、

    60      w1 = a;
    95      *cts = 1;
   113      *xx = xd;
  
と書くだけで済みます。1.0は、1と整数として書いても、*costhにはfloat型の1.0がちゃんと代入 されます。

では、代入時の型変換はどのように行なわれるのでしょうか。代入の時には、右辺の値が、左辺 の型に自動的に変換されます。したがって、定数値や変数値を代入するだけならば、決してキャス トはいりません。C言語の変数には型があるので、左辺と右辺の型が違えば、自動的に直してくれ ます。いちいちキャストをすると、プログラムが見にくくなるだけですね。

■演算時の自動的型変換■

次は、式中での演算時のキャストについて見てみましょう。

   98    *cts = (x2 - x1) / (float)d;
  202    gct = ((float)gpx[i2] - (float)gpx[i1]) / (float)d;
  207    xdd[jj] =  gct * (float)gpx[p] + gst * (float)gpy[p];
  
98行の x1,x2 はfloat型、d はdouble型なので、分子のfloat型に分母を合わせるため、分母の d を(float)でキャストしています。

202行は、分子の2つの gpx がlong型で、このまま減算を行なうと、結果もlong型になってしま い、その後の除算に都合が悪いと思い、まず、それぞれの gpx をlong型からfloat型にキャストで 直してから減算を行なっています。分母の d にキャストしているのは、110行と同じ思い込みから でしょう。

220行の最初のキャストは、float型の gct とlong型の gpx の乗算は直接はできないと思い込み、 long型の gpx をfloat型に直してから乗算を行なっています。2番目のキャストも同様の思い込み でしょう。

ところで、C言語の数値演算においては、自動的型変換(暗黙的型変換)が行なわれます。そも そも、C言語の整数型、浮動小数点型いずれにも、型の優先順位があります。異なるデータ型間で の演算を行なうときには、より優先度の高い型の方に型合わせをしてから演算します。この優先度 は、結局は精度であり、次のようになっています。

  char<short≦int≦long<float<double
  
浮動小数点数型は、必ず、どの整数型より高くなっています。

普通は、shortが16ビット、longが32ビットですが、intはコンピュータやコンパイラ、あるいは コンパイル時のオプションにより、16ビットになったり32ビットになるので、等号を入れています。

これに従うと、207行のような float型*long型のlong型は自動的にfloat型に変換されてしまう ので、わざわざfloat型にキャストしなくてもfloat型に変換されることが分かります。したがって、 float型*char型、float型*short型、float型*long型、いずれも演算結果の型はfloat型になります。 もちろん、逆順のchar型*float型などもfloat型になります。極めて自然でしょう。

202行はlong型同士の減算を行い、double型で直接除算を行なうのが自然のやり方です。long型 同士で減算を行なうと分子がlong型になってしまいますが、問題はないでしょう。分母のキャスト も止めてしまい、long型/double型にして、除算結果をdouble型で求めて、左辺のfloat型への代 入時に自動型変換してもらえばよいでしょう。これで、3つもあるキャストは全部不要になりまし た。右辺をdouble型で計算するのにムダを感じる人もいるようですが、一般的には、float型で計 算する方がムダといえます。理由は後で詳しく教えましょう。それに、float型をdouble型で計算 すれば精度は向上するので、困ることはまず無いはずです。

ということで、最初に示した式は、

   98    *cts = (x2 - x1) / d;
  202    gct = (gpx[i2] - gpx[i1]) / d;
  207    xdd[jj] =  gct * gpx[p] + gst * gpy[p];
  
というように、一切キャスト無しで良くなります。これで、とっても自然になったと思いませんか。

以上の説明に従うと、このプログラム中のキャストのほとんどは不要なことが分かります。キャ ストして、型変換を明示的にすることにはそれなりの価値がありますが、今回の例では、そのよう な個所はほとんど見当たりません。プログラムを見づらくしているだけのキャストばかりですので、 バシバシ消してしまいましょう。

そもそもキャストとは、mallocなどでデータ領域を確保し、その領域をある型の配列などとして 使用するために使うのが主たる使用法です。このプログラムみたいにキャストの多いプログラムは 今まで一度も見たことがありませんでした。どんなに複雑怪奇なデータベースを作っても、このよ うに多数のキャストが出てくることは有り得ないでしょう。


我輩は猫である



Copyright1996 Hirofumi Fujiwara. No reproduction or republication without written permission
『Cプログラミング診断室』目次次(第4章 キャストが好き float型対double型)