ce-15. 計算精度と誤
1
金子邦彦
C プログラミング応用)(全14回)
URL: https://www.kkaneko.jp/pro/c/index.html
int
C言語では,整数データは int 型などで扱
Microsoft Visual C++ では
int 4バイト
扱える値に範囲があ
-2147483648 2147483647 (-231 231-1)
符号で1ビット,残りで数を表現
2
オーバーフロ
それぞれの型 (int, double など)ごとに「表現可能
な範囲」が定まる
計算の途中で「表現可能な範囲」を越えてしま
こと オーバーフロー
3
少数点以下切り捨て
整数の演算において除算を行合、除算結果の
小数点以下は切り捨てられる。
(整数)/(整数)の場合のみ、この小数点以下の切り捨てが
行われる。
(整数)/(浮動小数点数)や(浮動小数点数)/(整数)
の場合は浮動小数点数型として計算されるので結果に
小数点以下の値も含まれる。
異なる型を含む演算では、必要に応じて自動的に型変
換が行われる。
予期しない結果を招かないためにも明示的に型変
換をした方がよい。
4
整数データと浮動小数データの違い
5
整数データ 浮動小数データ
変数
宣言
int kingaku;
int en;
double teihen;
double takasa;
入力 scanf("%d", &kingaku); scanf("%lf", &takasa);
出力 printf("kozeni: %d en\n",
en);
printf("takasa: %f \n",
takasa);
四則
演算
四則演算には,+, -, *, /
使
四則演算には,+, -, *, /
使
剰余 en = kingaku%1000; z = fmod(x,y);
1/2 の値は 0
6
#include "stdio.h"
int main()
{int ch;
double r;
r = 1 / 2;
printf("r = %f\n ", r );
ch = getchar();
ch = getchar();
}
このプログラムの実行結果は,直感とは一致しない
かも知れない
r = 0.000000
右辺に整数の変数しか登場しないので,右辺は整数
の精度で計算される
1.0/2.0 の値は 0.5
7
#include "stdio.h"
int main()
{int ch;
double r;
r = 1.0 / 2.0;
printf("r = %f\n ", r );
ch = getchar();
ch = getchar();
}
1 / 2 」と「1.0 / 2.0 は,意味が違
r = 0.500000
右辺に浮動小数の変数が登場するので,右辺は浮動小数
の精度で計算される
1/2 1.0/2.0 の違い
1/2 は,整数と整数の割り算
文法的には 2000/30 (値は66 」と書くのと同じ
1/2 の値は 0 (やはり整数)
1.0/2.0 は,浮動小数と浮動小数の割り算
1.0/2.0 の値は 0.5 (浮動小数)
8
0除算
ある数を0で割る(0除算)と、プログラムは異常終
了する。
調和平均を求める場合、入力データに0が含まれると、
0除算が起こり異常終了する。
割り算を行場合は割る数が0でないことを調べて
から行
入力データが0であることを検知して、調和平均の計算
を止めるよする。
9
浮動小数点数の演
10
計算機内での数値の表現
数値の桁数は変数の型によって決まっている。
int32ビット (Microsoft Visual C++の場合)
11
50(10) = 00000000000000000000000000110010(2) int
負の数
2の補数表現であらわす。
絶対値を決められたビット数で表す
01を反転する
1を加える
12
0000000000110010(2)
short int 型の例 (-50)
1111111111001101(2)
1111111111001110(2)
-50(10) =
50(10) =
1を加える
01を反転する
浮動小数点数
整数(正または負または0)あるいは小数付きの
0.5 ( = 0.5 ×100)
仮数(mantissa)部と指数(exponent)
13
仮数の整数の桁は1桁で0以外
23
10022.6
仮数
指数
基数(radix)
10進数の0.12進数で表すと
無限小数になる。
0.1(10)=
0.0001100110011001100110011001…(2)
1.100110011001100110011001…×2-4(2)
23ビットで表すために、小数第24桁目で01入を
するので、誤差が生じる(丸め誤差)
14
小数第24桁目
0 01111011 10011001100110011001100
= 9.9999994039535522×10-2
0 01111011 10011001100110011001101
= 1.0000000149011612×10-1 10-9程度の誤差
丸め誤差の集積(情報落ち
丸め誤差が集まると,誤差が増える
15
#include "stdio.h"
int main()
{
double x;
double s;
int ch;
x = 0.0001;
s = 10000;
for (int i = 0; i < 100000000; i++) {
s = s + x;
}
printf("1 0.0001100000000回足すと%fになります。\n", s);
ch = getchar();
ch = getchar();
}
出てくる値は 20000 にならない
浮動小数点でのループの終了判定
ループの制御を浮動小数点型変数を用いて行
合は注意が必要。
変数 x 0.001 きざみで01まで変化させて何かを処
理する場合(面積の近似計算など)
継続条件式を x <= 1 としても、1,000 回目のループが
実行されるかどかは確認が必要。
0.001 1,000 回加えても丸め誤差の集積で1以外の数になっ
ている可能性がある。
1より小さい場合は処理が実行される。
1より大きい場合は処理が実行されない。
ループ回数で制御することで回避する。
16
仕様
2次方程式を ax2+bx+c=0 として、係数abc
入力してもら
2次方程式の解の公式に従って、浮動小数点数解が
あるとき、2つの解x1x2を計算し、その値を出
力する。そのときの(ax2+bx+c)の値も表示する。
17
a
acbb
x
a
acbb
x2
4
2
2
4
122
プログラム
18
double a, b, c, d, x1, x2;
printf("Please input a b c : ");
scanf("%lf %lf %lf", &a, &b, &c);
d = b * b - 4 * a * c; /* 判別式の計算 */
if(d >= 0){ /* 判別式が正のとき、解を計算 */
x1 = (-b + sqrt(d)) / (2.0 * a);
x2 = (-b - sqrt(d)) / (2.0 * a);
printf("x1 = %17.10e (%17.10e)\n",x1,a*x1*x1+b*x1+c);
printf("x2 = %17.10e (%17.10e)\n",x2,a*x2*x2+b*x2+c);
}
else{ /* 判別式が負のとき、虚数解と表示 */
printf("x1 = 虚数解\n");
printf("x2 = 虚数解\n");
}
プログラムのテスト
係数の値として下の値を入力した場合の結果を観
察すると、
(1 2 1)
x1 = 1.0000000000e+000 (0.0000000000e+000)
x2 = 1.0000000000e+000 (0.0000000000e+000)
(1 1000 1)
x1 = 9.9999900000e+002 (1.1641532183e-010)
x2 = 1.0000010000e-003 (-2.0621060415e-011)
(1 1000000 1)
x1 = 1.0000000000e+006 (0.0000000000e+000)
x2 = 1.0000076145e-006 (-7.6144923700e-006)
(1 100000000 1)
x1 = 1.0000000000e+008 (1.0000000000e+000)
x2 = 7.4505805969e-009 (2.5494194031e-001) 19
x2の左辺
の値が0
から離れ
ていく
(誤差が
大きくな
る)
方程式の左辺の値
桁落ち
有効数字の桁数が減少すること
先の例の(1 1000000 1)では
|b| 0 412 e848000000000
√D 0 412 e847fffffbce4
0 412 000000000431c
0 3ec 0c70000000000
20
正規化
有効な桁が12ビットになる
もし桁数が限られていなければ、後ろの0が続く40
ビットにも値が入るはず。
桁落ち
絶対値がごく近い2数を足したり引いたりして、結
果の絶対値が小さくなるよな計算を行桁落
ちが起こる。
絶対値が小さくなった分だけ相対誤差が大きくな
り、有効数字が減る。
式の変形などで桁落ちを回避することができるこ
とがある。
21
2次方程式の解で桁落ちを防ぐ
|b|+√D の方だけを使って1つの解を求める。
b0 の場合
b0 の場合
1つの解は、解と係数の関係から求める。
22
a
acbb
x2
4
12
a
acbb
x2
4
12
1
2xa
c
x
改良版プログラム
23
double a, b, c, d, x1, x2;
printf("Please input a b c : ");
scanf("%lf %lf %lf", &a, &b, &c);
d = b * b - 4 * a * c; /* 判別式の計算 */
if(d >= 0){ /* 判別式が正のとき、解を計算 */
if(b >= 0) x1 = -(b + sqrt(d)) / (2.0 * a);
else x1 = (-b + sqrt(d)) / (2.0 * a);
x2 = c / (a * x1);
printf("x1 = %17.10e (%17.10e)\n",x1,a*x1*x1+b*x1+c);
printf("x2 = %17.10e (%17.10e)\n",x2,a*x2*x2+b*x2+c);
} else{ /* 判別式が負のとき、虚数解と表示 */
printf("x1 = 虚数解\n");
printf("x2 = 虚数解\n");
}
解と係数の関係より