Subsections

3章 演算と型

3.1 本日の課題

以下のプログラム(.cが付くファイル), および解答をメールで提出する. 本文に, 学籍番号, 氏名を明記すること.

  1. 3.3 に示す入出力関数 printf, scanf の使い方を理解する. とくに, 実数の出力形式を制御して, 使用している環境における double 型の変数の有効桁数を答えよ.

  2. 3.4に示すプログラムを実行した結果を答えよ. さらに, 各演算においてどの型変換の規則が適用されたかを答えよ. 暗黙の型変換および明示的な型変換(キャスト)については教科書を参照のこと.

  3. 変数とそれを用いた演算を行うプログラムを作成せよ. 3.5に示す例を参考にしてもよい.

3.2 整数と実数

整数
整数の例: 10 -10 +10

実数
1.1.0 のように整数と区別するため後ろに . または .0 を付ける. 桁数が大きな実数を表記する場合には 1.2e34のように指数表示を用いる. この例は $1.2 \times 10^{34}$ を表す. 実数の例: -1., -1.0, 1.2, -1.2e34, 1.2e-34


3.3 printf, scanfの書式

3.3.1 書式のまとめ

整数および実数を入力場合, 出力する場合の書式を覚えましょう.

int a;
double x;
とするとき,
  整数 実数
出力 printf("a = %d\n", a); printf("x = %f\n", x);
入力 scanf("%d", &a); scanf("%lf", &x);

3.3.2 書式による詳細な出力制御

printf関数の第一引数を与える書式では, 出力桁を制御できる.

整数の出力形式
%23d のように %d の間に整数 nを指定することで, 出力する文字数 (桁数) を制御できる. ここで n は全体の桁数. これにより桁を揃えた出力が可能になる.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	int x = 21;
	int y = 4321;
	int z = 1;

	printf("x=[%7d]\n", x);
	printf("y=[%7d]\n", y);;
	printf("z=[%7d]\n", z);
	
	return EXIT_SUCCESS;
}

実数の出力形式
実数を出力する場合には %fの他に %e および %g が有用.
%e 1.2e-34 のような指数表示. 

%g 12.00000 のように必要のない 0 が続くときは 12 のように省略する.
   また, 自動的に 桁が小さい実数は 12, 0.0034 のように出力し,
   桁が大きい実数は 5.6e78 のように出力する.

   例 printf("a=%f\n", a);
      printf("a=%e\n", a);
      printf("a=%g\n", a);

%10.5f のように %f の間に n.m のように整数 n, m を指定することで, 出力する文字数 (桁数) を制御できる. ここで n は全体の桁数, m は小数点以下の桁数.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	double x = 3.4e56;

	printf("x=[%30.22f]\n", x);
	printf("x=[%30.22e]\n", x);
	printf("x=[%30.22g]\n", x);

	printf("x=[%f]\n", x);
	printf("x=[%g]\n", x);
	
	return EXIT_SUCCESS;
}


3.4 暗黙の型変換とキャスト

以下のプログラムを実行した結果を答えよ. さらに, 各演算においてどの型変換の規則が適用されたかを答えよ.

#include <stdio.h>

int main(void)
{
	int a, b, c;
	double x, y, z;

	a = 7;
	b = 3;
	
	x = 7.0;
	y = 3.0;

	c = a/b;
	printf("[1] %d\n", c);

	z = a/b;
	printf("[2] %f\n", z);
	
	z = x/y;
	printf("[3] %f\n", z);

	z = a/y;
	printf("[4] %f\n", z);

	z = x/b;
	printf("[5] %f\n", z);

	c = x;
	printf("[6] %d\n", c);

	c = x/b;
	printf("[7] %d\n", c);


	z = (double)(a/b);
	printf("[8] %f\n", z);

	c = (double)(a/b);
	printf("[9] %d\n", c);

	return 0;
}


3.5 変数と演算

変数を用いて以下のような量を求めてみましょう.

長さ
正方形の周囲の長さ. 長方形の周囲の長さ. 円周の長さ. 円弧の長さ.
面積
三角形の面積. 台形の面積. 正方形の面積. 長方形の面積. 円の面積. 扇形の面積.
体積
三角柱の体積. 四角柱の体積. 円柱の体積.
体積
三角錐の体積. 四角錐の体積. 円錐の体積.
関数値
$x$が与えられた時の 関数 $f(x) = x^2 + 1$ の値. $x, y$ が与えられた時の関数 $f(x, y) = x^2 + y^2$ の値.
方程式の解
$a, b$ が与えられた時の 1次方程式 $a x + b = 0$ の解. $a, b, c$ が与えられた時の 2次方程式 $a x^2 + b x + c = 0$ の解.
テイラー展開
テイラー展開により関数の値を近似的に求める.
統計
5つの実数の和. 5つの実数の平均値.
エネルギー
質量, 速度が与えられたとき質点の運動エネルギー.
単位の変換
時間を分に変換. J を cal に変換.
その他
いろいろな量を計算してみましょう.

例:

// プログラム中で 底辺の長さと高さを与え, 三角形の面積を求めるプログラム.

#include <stdio.h>

int main(void)
{
    double base, height, area;

    base = 5.0;
    height = 2.0;

    area = base * height / 2.0;
    printf("面積は %g です。\n", area);

    return 0;
}

// キーボードから 底辺の長さと高さを入力し, 三角形の面積を求めるプログラム.

#include <stdio.h>

int main(void)
{
    double base, height, area;

    printf("三角形の面積を求めます。\n底辺の長さを代入してください:");
    scanf("%lf", &base);
    printf("高さを代入してください:");
    scanf("%lf", &height);

    area = base * height / 2.0;

    printf("面積は %g です。\n", area);

    return 0;
}

// Taylor 展開により sin(x) を x=0 の近傍で近似する.

#include <stdio.h>

int main(void)
{
	 double x, y;

	 x = 0.2;
	 y = x - 1.0/(3*2*1) * x*x*x + 1.0/(5*4*3*2*1) * x*x*x*x*x
		  - 1.0/(7*6*5*4*3*2*1) * x*x*x*x*x*x*x;

	 printf("sin(%g) = %g\n", x, y);
	 
	 return 0;
}

// 1次方程式 a x + b = 0 の解を求める.

#include <stdio.h>

int main(void)
{
    double a, b, x;

    a = 1.0;     // a は 0 でないこと.
    b = 3.2;

    x = -b/a;

    printf("a=%g, b=%g, x=%g\n", a, b, x);

    return 0;
}

/*
  2次方程式 a x*x + b x + c = 0 の解を求める.

  sqrt は平方根を求める数学関数.
  数学関数を用いる場合は 予め math.h をインクルードする.

  gcc でコンパイルする場合は 数学関数のライブラリ libm に含まれる
  数学関数 sqrt を使用するので, このファイルの名前を a.c とすると
  cc a.c -lm
  のように -lm オプションが必要.
*/

#include <stdio.h>
#include <math.h>

int main(void)
{
    double a, b, c, D, x1, x2;

    a = 1.0;
    b = -4.0;
    c = 3.0;

    // a は0でない数としかつ D は負にならないよう a, b, c を与えること.
    D = b*b - 4.0 * a * c; 

    x1 = (-b+sqrt(D))/2.0/a;
    x2 = (-b-sqrt(D))/2.0/a;

    printf("a=%g, b=%g, c=%g\n", a, b, c);
    printf("D=%g\n", D);
    printf("x1=%g, x2=%g\n", x1, x2);

    return 0;
}

3.6 コラム: 変数の型, 書式 の整理

変数の型を整理してみると,なぜ実数を double により宣言するのか. scanf, printf の書式でなぜ 整数や実数を %d, %lf により指定するの かが見えてくる.

ここでは, C言語の規格では明示されていないが, 多分こんなことだろうということを書いてみる.

実数はなぜ double で宣言するの?
実数として,単精度実数 float とより有効桁数の多い倍精度実数 double がある. 最近では, 小さなプログラムを動かすだけであれば, 変数がコンピュータのメモリを使い切ってしまうことは起きない. また, float 型実数は, 一旦 double 型変数に変換されたのち演算を行い, float 型に戻すという実装もある. この場合, 変換に必要な時間だけ実効速度は遅くなる. そこで, 実数としては double を用いることが広く行われるようになったようだ.

scanf ではなぜ実数を %lf で指定するの?

単精度実数 float は %f により指定する:

    float x;
    scanf("%f", &x);
倍精度実数 double は float よりサイズが大きい. C言語ではサイズが大きい変数したがって有効桁が多い変数は long により修飾し宣言し, printf や scanf の書式では l (エル)により修飾することになっている. double 型は float 型に対して long であるので %lf で指定するようになったようだ.

printf, scanf ではなぜ整数を %d で指定するの?
printf や scanf の書式では, 整数は %d により指定される. d は何を意味しているのだろうか. 表に示すように整数の型を整理してみると, 整数には signed と unsigned がある. これらは異なる指定をする必要があるので, 整数を %i で出力するわけにはいかない. そこで 両者を区別するために, unsigned int は %u, signed int は %d で表すことになったようだ.

変数の型

サイズ 通常
符号無し singed short int signed int signed long int
符号有り unsinged short int unsigned int unsigned long int

整数の型(省略形)
サイズ 通常
符号無し short int long
符号有り unsinged short unsigned unsigned long

実数の型
サイズ/精度 通常 倍より大きい
  float double long double

(c)1999-2013 Tetsuya Makimura