前回で型を使って変数に数値(定数)を入れる事が出来ました。この変数内の数値を計算してみます。計算と言えば、日本人が小学校(幼稚園か?)からひたすら学んできた基本中の基本である、四則演算から始めてみます。
--
いきなりここに飛んで来ちゃった人は、よろしければ下記からご覧ください。
C言語基礎講座インデックス


  • 演算の種類
四則演算と言えば、足し算(加算)、引き算(減算)、掛け算(乗算)、割り算(除算)がそれにあたります。コンピュータの世界では、米国さまから知識が渡ってきた事もあってか、足し算と言わず加算というのが普通(多数)なので、これ以降、本講座でも加減算や乗除算等と記載します。

この四則演算を行う命令を算術演算子と呼びます。…が、例によってこんな呼び方なんて覚える必要はありません。大事なのは計算するために必要な書き方です。数値代入は右から左でした。計算も同様で、右側で計算した結果を左側の変数に代入する事で行われます。加算、減算、乗算、除算は、算数の計算式では +,-,×,÷ ですが、アメリカの最初の記号にこの文字が全てが含まれていなかったため、+, -, *, / で代用されています。

とりあえず算数のお約束 1 + 1 を C言語で記述してみます。

int ans; ans = 1 + 1;
この結果、ans には 2 が格納されています。算数の数式を見慣れてる人には、これ、気持ち悪く見えるかもしれません。変数宣言でも説明したとおり、= はイコールじゃ無くて代入なんです。
※ 代入演算子と呼びます。
変数宣言と計算を別々に書きましたが、変数宣言時に予め値を入れておく事も出来ます。例えばこんなプログラムを書いてみます。

int n1 = 10; int n2 = 5; int ans = n1 / n2;
n1 が 10、n2 が 5 になっています。そして ans は n1 / n2(10 ÷ 2)が計算されますので、結果として 2 が格納されます。

文字も数値で管理していますので四則演算できます。例えば A ~ Z を連番に変更したい場合などは

char moji = 'R'; int num = moji - 'A';
この結果、num は 17 が格納されます。

char moji = 'A'; moji = moji + 1;
この結果は、moji の値に +1 されて、また moji に戻ります。前回のキャラクタコード表を見ると、'A'(0x41)の次である 0x42 は 'B' なので、変数 moji の中身は 'B' になります。


  • 型変換
ちょっと意地の悪い計算をしてみましょう。

int n1 = 10; int n2 = 3; int ans = n1 / n2;
この時、ans には 3.333333 が入りまぁー…せん。理由は ans が int 型だからです。int は整数型なので実数が入らないのです。このような計算が行われた場合は、小数点以下が捨てられて、整数部分の 3 が ans に格納されます。これを暗黙の型変換と言います。この自動変換はきっちり意識しておかないと、思わぬバグを生む事があります。暗黙の型変換系のバグは見つけにくいのでご注意ください。

暗黙の型変換を最初に教えているサイトは少ないのですが、これを最初に説明しなくてどうすんだとは私は思います。勝手に変換される事は、先に教えといてくれよと。この暗黙の型変換でハマりやすいポイントを説明します。まずは以下の計算結果がどうなるか想像してみてください。

int n1 = 10; int n2 = 3; float ans = n1 / n2;
ans の型は実数です。結果、3.333333 となりそうなものですが、実際には 3.000000 となります。これは右側の計算が全て整数で行われているためです。整数演算の結果、3 という値が出来て、それを ans に代入しているため、期待している値が入らなかったのです。この場合は明示的に型変換を指定します。この場合だと、

int n1 = 10; int n2 = 3; float ans = n1 / (float)n2;
このように n2 を float として扱うように記述すると、右辺の計算が実数で行われるので、ans  には 3.333333 という数値が格納されます。これをキャストするとかキャストされたとか呼んでいます。日本語だと明示的な型変換ですね。
※ DVD / BDドライブです。薄型より全然高いんですが、使うのなら断然こちらです。とにかく読み書きが高速です。内部ドライブは本来デスクトップPC用のヤツなので、速いです。個人的にはこれ一択です!

  • 実数の誤差
float は実数を扱えると説明しました。では、その有効範囲はどうなっているのでしょうか。リファレンスによると float の有効範囲は 3.4E +/- 38 (7 桁) という記述があります。コンピュータは整数だろうと実数だろうと、その内部では2進数で値を処理している関係で、かならず演算誤差(表現の限界)がつきまといます。そして、その float の有効桁数は小数点以下6桁までなのです。上記で私が 3.333333 としか書かなかったのは、そういう理由なのです。

実数はコンピューターでは浮動小数点形式で扱われています。この浮動小数点形式とは、32bit の値で効率良く実数を扱えるように制定された形式です。実際のところ、私はこの浮動小数点形式の仕様は覚えていません。覚える必要が無かったためです。興味があればこちらを参照してみてください。

話を戻して、先の計算結果で得られた ans の値を、VS2022 で中がどうなっているかを覗くと 0.33333325 となっていました。
C005 floatの有効範囲
小数点以下7桁目からはおかしな数値になっていますよね。この誤差は意外と無視出来ないのです。昔、ラリーゲームで Intel の CPU でリプレイを作ったところ、Intel の CPU だとゴールまで走り切るのに対して、AMD CPU だと途中の崖から落ちるという事件がありました。ゴルフのリプレイで、ホールインワンしたのにリプレイではカップインしなかった事もあります。これらは全て浮動小数点演算の誤差が積み重なった結果なのです。
CrashCar
※ 有効桁数以下の数値がどうなっているかはCPUや処理演算系で異なっています。

今の段階では、皆さんのプログラミングの範囲で誤差が問題にならないと思いますが、ある日突然、この問題はいきなり発現しますので、心の片隅に入れておいてもらえるとよろしいかと。


  • 剰余算
四則演算以外に C言語に用意された演算が剰余算です。簡単に言えば、割った余りを求める演算です。この演算には % が割り当てられています。例えば、10 / 3 の結果は、3 で余りが 1 になりますが、この 1 を得られるのが剰余算なのです。

int div = 10; int add = div % 3; /* 3で割った余り */
この結果、add には 1 が格納されます。この剰余算は本当によく使います。例えば、数値が奇数か偶数かを調べる場合は、

int num = 123; int odd = num % 2; /* 奇数偶数判定 */
この結果、odd が 1 なら奇数と分かります。数値が不明な変数を 0 ~ 15 の範囲に置き換えたい場合は、

int num = 12345; int hex = num % 16;
これで hex には 0 ~ 15 の値が入ります。上記のプロクラムで /* 言葉 */ という部分が出てきました。これはコメントと呼ばれる部分です。/* から */ に挟まれた部分は、コンパイルでは一切参照されません。そのため、私たち人間(プログラマー)の理解を手助けするメッセージを書いて置く事が出来ます。決して落書きに使用しないように…

※ 私の教え子の提出課題に /* 徹夜覚悟 */ /* まだ頑張る */ /* 外が白々としてきた */ /* そろそろ限界 */ とか書き残した学生がいましてね。日記じゃねぇしwww


  • まとめ
  1. 基本的な演算は、加算、減算、乗算、除算、剰余算がある
  2. 型の変換は、暗黙の型変換とキャスト(明示的な型変換)がある
  3. 暗黙の型変換には注意する
  4. 実数の誤差は気にする
>> C言語基礎講座インデックスに戻る

良かったらポチって頂けるとやる気が上がります!ご協力感謝です!!

※ 本来はラズパイをキビキビ動かすためのアダプターですが、これを使って某ミニを動かすと遅延が減ったとか、動きが良くなったとかと私の界隈で凄く評判です!…但し、私は効用を知りません。デマかもしれませんし、信じるモノは救われる?(足下を?