計算機言語 I 第 4 回 Switch 文 , レポートに対するコメント
この資料
: http://www.math.u-ryukyu.ac.jp/~suga/C/2020-1/06.pdf
1 レポートへのツッコミ : 2 の補数表現と整数値の読み込み
負の整数の扱い
: 2
の補数IEEE–754
の浮動小数点型の数では,
数の正負を示すための符号ビットが存在しました.
整数型では,
これとは少し異なる符号の表現を用います
.
今回は,
多くのCPU
で採用している,
「負の整数」の取り扱い方法であ る,
「2
の補数」の話をします. 32bit
では桁数が大きすぎるので,
簡単のために4bit
で話をします.
ここでは, 2
進法表記の数は,
数字の最後に添字2
をつけることにします.
例えば, 110
2= 1 × 2
2+ 1 × 2
1+ 0 × 2
0= 6
です.
ここで述べる話は
,
多くのCPU
で実装されているハードウェアの話です. 4bit
しかない計算機では, 1111
2+ 0001
2= 0
が成立します.
この計算(
筆算)
では繰り上がりが起こりますが,
繰り上がるべき5bit
目が無いので
, CPU
内では捨てられます.
つまり, 1111
2はこの計算機の加法では, − 1
と同じ意味を持ちます.
これを利用して負の数を表す方法を
,
「2
の補数」と言います.
この計算機では, 4bit
の最上位ビットを符号であ ると思い,
ここが0
なら正整数, 1
なら負整数と見ます*1. 4bit
で0011
2は+3, 1011
2は, − 5
です. 2
の補数で は,
整数値の符号を変えるには,
全てのbit
で, 0
と1
を入れ替えた(bit
反転という)
数に1
を加えるという操 作になり, CPU
はこの動作を実行します*2. 4bit
の2
の補数の整数では,
最大の整数は0111
2= 2
3− 1 = 7,
最小の整数は, 1000
2= 2
3= − 8
で,
扱える数の範囲が0
に対して対称では無くなります.
最上位bit
が1
である数の値は, 4bit
の場合, 2
4を0
として利用しているので,
符号無しの2
進数としての値をx
とすると, x − 2
4となります.
数学的に言うと, mod 2
4, ( Z /2
4Z )
の計算をして,
最上位ビットの値で正負の判断をして いるわけです.
現時点の
CPU
では,
上の2
の補数を用いた整数計算が, 32bit
もしくは64bit
でCPU
内で実装されてお り,
多くのプログラム言語では,
これをそのまま利用しています. CPU
内の計算の実装は,
「overflow(
桁あふ れ)
は無視して(
桁あふれしたらそれは捨てる),
単純に正の2
進法の数としての計算をする(
これは,
掛け算な どの他の計算でも同じ)
」という形になっています.
すなわち, Z /2
nZ
の計算が実装されているのです.
このよ うな整数計算の実装が標準になったのは, IBM
がSystem/360
で採用したからだと言われています.
皆さんの
C
言語の処理系では, int
型は32bit
の2
の補数で処理されます.
上の4bit
の例から分かる通り,
この場合, int
型の範囲は, 2
31− 1
から− 2
31 となり,
これが教科書p. 30
の表に書いてある値です.
*1実際には単純に捨てるのではなく,オーバーフロービット(overflow bit)用意して,桁あふれを起こしたという情報を保持するこ ともある.
*2「符号を変える操作=bit反転」とする符号の決め方もあり,「1の補数」と呼ばれます. ハードウェア的にはこの方が単純で,扱 える整数の範囲も0に対して対称なのですが, +0 = 0...0002と−0 = 1...1112の正負の0が生じる問題があります.
1
なお
,
整数値を符号無しとして扱う場合もあり,
その場合には最上位bit
も込めて正の整数値とします.
上の4bit
だと,
符号無し整数は, 0
から15 = 2
4− 1
の値をとります. 32bit
だと0
から2
32− 1
の値になります.
キーボードからの読み込み
皆さんが画面で見ている文字は
, Ascii
コードやUTF–8
の文字コードを文字として表示しています.
画面 に「1
」(
半角)
が表示されているときには, Ascii
コードの(UTF–8
でも同じ)
「1
」に対応する数を文字として 表示しています.
scanf("%d", &input);
というソースコードでは
,
キーボードからの入力(
計算機概論で述べた標準入力)
を,
整数値に変換して,
変数input
に格納します.
キーボードからの入力も文字コードです.
文字コードの文字を対応する数に変換するのは
, 0
の文字コードを引き算すれば得られます. C
のソースコードでは,
例えば, ’9’-’0’ = 9
です.
さて
,
キーボードから987654321
という文字が入力されたとき,
これを整数値に変換するには,
次のような 動作を行って数を作ります.
各桁の文字コードを上の操作で対応する数に変換した後,
次の計算をします.
9 → 9 × 10 + 8 → (9 × 10 + 8) × 10 + 7 → (((9 × 10 + 8) × 10 + 7) × 10 + 6 → · · ·
上の計算法を, Horner(
ホーナー)
の方法と言います.
上のような計算をライブラリ関数が実装しているわけですが
,
その際にoverflow
は無視して,
単純に2
進数 の積と和を計算します.
また,
負符号があった場合,
それは一番最初に読まれますから,
その時に符号ビットを セットしますが,
その後は符号無し整数としてのハードウェアによる計算が実行されます.
前回のレポート問題の解答
前回のレポート問題は
, int
値を標準入力から読み込んで,
それをそのまま出力するプログラムと,
そのプロ グラムで, 300000000(30
億)
を入力した時の出力を問う問題でした.
3000000000
を入力した時,
上に述べた「文字列 → 数字」変換操作が実行されます. 300000000(3
億)
はint
型の範囲なので,
ここまでは特に問題なく正しい数値に変換されます.
次の0
を読んだ時,
この数が10
倍 されますが,
これはint
型の範囲を超えます.
この時,
内部的には300000000(3
億)
の2
進数を(
符号や桁あ ふれを無視して)
そのまま10
倍します.
つまり,
符号を無視して3000000000(30
億)
の2
進数の値になりま す.
その2
進数の最上位bit
は1 (30
億> 2
31(
約20
億))
になりますから,
上の「2
の補数」のルールより,
負の数と解釈されます.
その値は,
上のルールから3000000000 − 2
32= − 1294967296
になります.
なぜ上のような実装になったのか
?
歴史的な流れで上のような整数型の実装が主流になったようですが
,
その明解な理由を私は知りません.
こ こに書くのは私の想像なのですが,
次のようなことが理由ではないかと思われます.
•
整数や実数を完全に扱える理想的な計算機をハードウェアで実現するのは,
おそらく不可能であろう.
•
コンピュータ初期の製造技術では,
理想に近いものを作るよりも,
実装のしやすさの方が重視された.
•
上のような実装でも,
ほとんどのプログラムは,
その実装をそのまま利用しても問題は起きない.
•
上のような実装を用いたソフトウェアが多く作られ,
その資産を継承したかった.
•
実際の問題を解く際には,
理想と実装のズレは,
ソフトウェアで解決すれば良いと判断した.
2
C
のライブラリ関数でも,
文字列 → 数値 の変換部分で,
整数値の桁あふれは無視しています.
これも,
桁あ ふれが起こるか否かは,
それを利用するプログラマしかわからないので,
そのプログラマが対処すべき内容だ との考え方をしています.
現実問題として,
人間は結構好き勝手は過ちを犯すので,
入力エラーにきちんと対処 するのは結構大変で,
この講義では,
そのようなことは扱いません.
C
の規格では, scanf()
で桁あふれを起こしたときの動作は未定義のようです.
上のようにならない実装も あり得ます.
2 Switch 文
前回
, if, if–else
で条件分岐をする方法を学びました.
今回は
,
もう一つの条件分岐の方法であるswitch
文です. switch
文は,
数学でよくやる「場合分け」を記 述するのに便利です.
全ての条件分岐は
, if–else
で記述できるのですが,
分岐の数が多かったり,
条件が複雑になると,
プログラ ムの記述も複雑になります.
そのような場合に,
これから述べるswitch
文を利用して,
プログラムのわかり やすい記述が可能になる場合があります.
エディタで記述されたプログラムソースは
,
その言語の処理系と人間が読みます.
処理系の方は機械ですか ら,
複雑に書かれていても論理的に間違っていなければ,
その処理を実行します.
しかし人間は,
たとえ論理的 に正しくとも,
複雑に書かれていればそれを理解するのは大変です.
条件分岐のプログラムでは, switch
文,
if–else
を上手に用いて,
人間が理解しやすくプログラムするように心がけることが重要です.
switch
文を用いると,
教科書p.43, p.45
のフローチャートのような処理をより明解に書くことができま す.
使い方は, p.47
にある通りで, switch
文のフローチャートは, p.48
にあります. switch, case, break, default
は, C
の予約語(
キーワード)
で,
変数名や関数名には使えない単語です. default
は,
どのcase
に も当てはまらない場合の処理を書くための印です.
break
break
はswitch
以外の場所でも用いられるキーワードです.
その意味は,
「処理をそのブロック( { , }
で囲 まれる部分)
から抜けて次に移る.
」です.
従って, switch
文でも, break
があれば,
その時点で処理が次のブ ロックに移りますが, break
が無ければ,
処理はそのブロックの次の行に移ります.
その違いが, p. 48
のフ ローチャートの違いです.
教科書
p. 49
プログラム3.7, p, 50
プログラム3.8
をコンパイル,
実行してください.
3 条件演算子 (3 項演算子 )
C
には,
条件演算子(3
項演算子)
と言うものがあり,
次の形を取ります.
条件式?
式1:
式2
これは文として値を取り
,
条件式が真なら 式1
の値に,
偽ならば式2
の値になります.
使い方は, p.51
プログ ラム3.9
を見て下さい.
使い方によっては, if–else
を用いる記述よりも短くて簡明な記述になることがわか ります.
3
レポート問題
教科書
p.54
に2
次方程式の根を計算するプログラムが問題となっています.
次をレポートして下さい.
•
この問題の解答は教科書にありますが,
その解答プログラムを用いて,
方程式0.1x
2+ 10000.0001x + 10 = 0
の根を(
必要なら15
桁程度)
表示した結果.
•
上の根と真の根がずれているなら,
このような例の場合でも,
より真の根に近い根を表示するプログ ラム.
件名