(2e-10+2e+10)-2e+10と 2e-10+(2e+10-2e+10)の二つの実数の計算でした。 数学的には、二つの計算結果は等しくなるはずですが、 計算結果は等しくならなかったはずです。
計算機内部では、
という表現方法が用いられています。
nを仮数部とよび、mを指数部と呼びます。もちろん、それぞれは有限です。
従って、Pascalの実数のもつ精度も有限です。そのために
小さな実数2e-10と、それと比較して圧倒的に
大きな実数2e+10とを足し算してしまうと、
結局、2e+10になってしまいます。
このような理由により、
2e-10+(2e+10-2e+10)が、2e-10になるのに対して、
(2e-10+2e+10)-2e+10は、0.0になってしまいます。
Pascalという言語に限らず、計算機の上での実数計算はどうしても誤差が入っ てきてしまいます。計算機上での実数計算をするときには誤差のことを常に気 にとめましょう。
以前、変数は「データを格納する箱」と説明しました。代入文は、変数という 「箱」には式の計算結果を格納するという操作を意味します。
変数名 := 式この文が実行されると、左辺の変数に、右辺の式の計算結果の値が格納されま す。左辺の変数の型と右辺の式の型は同一でなければなりません。
例えば、nという整数型の変数があったときに代入文
n := 1が実行されると、変数nには整数値1が格納されます。
n := n + 1が実行されると、式n + 1の計算結果の値が変数nに格納されます。 すなわち、変数nという「箱」の中身の整数値が1だけ増やされる わけです。
Pascalの文法事柄の中で、式と文は非常に重要な概念です。文は「〜を
実行せよ!」という計算機に対する命令です。その実行結果は変数の値の変化
や(画面などの)入出力装置の状態の変化として得ることができます.一方、
式については式の値(value)が計算結果となります。
次に、式がどのように組みあげられていくかという文法的な側面について見て いきましょう。
式は演算子により結合されることにより更に大きな式を構成します。たとえば、 (1+2)*3では次のようになります:
1と2が式であり、それが、+という演算子により、 1+2という式が構成されます。そして更にこの式と3が、*という 演算子により、(1+2)*3という式を構成しているわけです。
式は、変数や定数を基にして、演算子や関数を使って組みあげられていくわけ です。ここではとりあえず演算子にしぼって解説していきましょう。
式と式を演算子で自由自在に何らの制限なく結合できるわけではありません。 演算子が結ぶことができる式には、それが表現する型(データの種類)により制 限があります。ある演算子は、整数型の式だけを結合することができたり、ま た別の演算子は、実数型の式だけしか結合できなかったりします。この点に注 意して、以下で紹介する演算子の説明を読んでください。
算術演算子(arithmetic operator)には次のようなものがあります。
積*、整数商div、余りmod、和+、符号の維持(プラ ス)+、差-、符号の反転(マイナス)-。
気をつけなければならないのは、結ばれる式の型(データの種類)です。
以下の演算子は、被演算子(結ばれる式)の型が両方が整数のときは、結果型 (結ばれてできる式の型)は整数型です。
次の2つも前の3つと同じですが、前の3つは実数型の被演算子を取りうる(後述) のに対して、次の2つは被演算子は整数型のものしか許されません。
次の4つの被演算子は、実数型である被演算子(演算子で結ばれる式)をとるも のです。最初の3つは、2つある被演算子のうち1つ以上が実数型であるときに、 結果の型が実数になります。両方とも整数型のときには、前述のとおり結果型 は整数型になってしまうことに注意してください。
1 + 1という式の計算結果と1 + 1.0という式の計算結果を表示 するプログラムを実行して結果を比べてみましょう。
あと、関係演算子と論理演算子という演算子があります。これは、 if文を紹介したあとで、説明しましょう。
次に示すプログラムは整数の和差積商と剰余を計算するものです。
program arithmetic(input,output); var i, j : integer; begin read(i,j); writeln('i = ', i); writeln('j = ', j); writeln('i + j = ', i + j); writeln('i - j = ', i - j); writeln('i * j = ', i * j); writeln('i div j = ', i div j); writeln('i mod j = ', i mod j) end.
次に実行例(2と3との計算例)を示します。
sino% pc arithmetic sino% ./arithmetic 2 3 (←これが入力された2つの整数) i = 2 j = 3 i + j = 5 i - j = -1 i * j = 6 i div j = 0 i mod j = 2 sino%
次に、2と0とを与えて実行すると次のようになります。
sino% ./arithmetic 2 0 i = 2 j = 0 i + j = 2 i - j = 2 i * j = 0 i div j = libm-290 : UNRECOVERABLE (訳:復旧不能) Scalar integer value divided by a scalar integer zero. (訳:整数値が整数0により割り算されてしまいました) Abort (core dumped) sino%
``Abort''というのは日本語訳は「失敗」ですが、Unixでのエラーの種類の一
種で、異常終了の場合に起るものです。 数学では、0による割り
算は未定義ですが、Pascalではエラーが発生してプログラムが異常終了してし
まいます。
このようなことが発生しないようにするためには、二番目の整数が0ではない かあらかじめチェックしておくことが必要になります。ここでは、そのような ときには割り算は計算せずに警告文を表示するようにプログラムを変更するこ ととします。
program arithmetic(input,output); var i, j : integer; begin read(i,j); writeln('i = ', i); writeln('j = ', j); writeln('i + j = ', i + j); writeln('i - j = ', i - j); writeln('i * j = ', i * j); if j = 0 then writeln('The 2nd input should be non-zero.') else begin writeln('i div j = ', i div j); writeln('i mod j = ', i mod j) end end.
ここでは、整数型変数jの値が0でないかどうかチェックをするために、 if文というものを利用しています。
if文は、
if 論理型の式 then 文1 else 文2という形をしていてます。「論理型の式」は、計算すると論理値(true もしくはfalse)というものが結果の値となるような式です。
j = 0は、変数jが整数0と等しいときにtrueという 値が計算結果になり、そうでないときfalseという計算結果になります。 イコール=が関係演算子とよばれるものの一種です。
if文の意味は、「論理型の式」の計算結果がtrueのとき「文1」が実行 され、そうでないとき、「文2」が実行される、というものです。
従って、プログラム例のような
if j = 0 then 文1 else 文2というif文では、変数jの値が0に等しいときには、文1が実行されて、 等しくないときには、文2が実行されるのです。
このプログラムでは、「文1」でなすべき作業は警告文を表示するという作業
だけですから、writeln文一つですむわけですが、「文2」では商の表
示をするwriteln文と余りの表示をするwriteln文の二つの文を実
行する必要があります。このようなときに用いられるのが複合文です。
複合文は、
begin 文1 ; 文2 ; … 文n end.というふうに、1つ以上の文をセミコロンで区切ったものをbeginと endで囲んだものです。1つのときは単にbeginとendで囲むだけ です。複合文の意味は、並んだ文を前から順に実行するというものです。
従って、
begin writeln('i div j = ', i div j); writeln('i mod j = ', i mod j) endという二つのwriteln文から構成される(一つの)複合文は商を表示した あと、剰余を表示するわけです。
=というように二つの式の値を比較する演算子として関係演算子 というものが用意されています。
被演算子(値が比較される式)の型には、整数型や実数型の他、論理型や文字型、 文字列型なども許されれます。被演算子のうち一つが実数型で、もう一つが整 数型の場合には、整数型の被演算子が実数に変換された後に比較されます。
次に示すプログラムは、二次方程式
の係数a,b,c( )を実数値の入力として読みこんで、
実根が存在するときには2つの実根を出力するプログラムです。
2次方程式の根の公式によると判別式を
とおくと、二つの根は、
ならば
D < 0ならば
でした。これに素直に従ってプログラムを作成すると、
program eq1(input,output); var a, b, c, d, sd, x1, x2 : real; begin read(a,b,c); if a = 0 then writeln('a should be non-zero') else begin d := b*b - 4*a*c; if d >= 0 then begin sd := sqrt(d); x2 := (-b - sd)/(2*a); x1 := (-b + sd)/(2*a); writeln(x1); writeln(x2) end else writeln('Imaginary root') end end.となります。ちなみに、sqrtは、規定関数(あらかじめ用意されている 関数)であり、sqrt(実数式)で、実数式の平方根(ルート)を意味します。
ヒント: writeln(x1, a*x1*x1 + b*x1 + c);そして、
の二つの場合の根と検算結果を求めなさい。
を用いて計算するプログラムeq3も作り、前と同様の検算を行ってみて、 二つの計算方法を比較してみなさい。
プログラムeq1、eq2、eq3の3つのプログラムとその実行 結果を添えて、レポートを作成してください。実行結果について考察したこと があれば、それについても記しておいてください。
kadai4というSubjectで、7月11日(金)までに電子メールで提出し なさい。