2008年度・前期・数理解析・計算機数学2・第1回 1 ● 講義資料 ▼ サンプルプログラム 計算機内部の浮動小数演算では, 我々の通常の感覚とは異った計算になることがある. この例では 0.1 を 10 回加えても 1.0 にならないことがわかる. 【C (1)】 /******************************* * $Id: ex_01_01.c,v 1.2 2006-04-10 13:37:00+09 naito Exp $ * 浮動小数点演算の例(倍精度浮動小数点) * 0.1 を 10 回加えても 1.0 にはならない *******************************/ #include <stdio.h> int main(int argc, char **argv) { double x = 0.1, y = 0.0 ; int n ; for(n=0;n<10;n++) y += x ; printf("y = %f\n", y) ; if (y == 1.0) printf ("y equals to 1.0\n") ; else printf ("y does not equals to 1.0\n") ; printf("y = %.16f\n", y) ; return 0 ; } コンパイル・実行方法 gcc ex_01_01.c -o ex_01_01 ./ex_01_01 結果 y = 1.000000 y does not equals to 1.0 y = 0.9999999999999999 注意事項 • double は「倍精度浮動小数点型」である. • 「単精度浮動小数点型」は float である. • 「浮動小数点定数」 0.1, 1.0E-1 などの表記は double 型定数であり, float 型定数として 0.1 を書 くときには 0.1F, 1.0E-1F など書く. ex01.tex,v 1.5 2008-04-14 18:00:24+09 naito Exp 2008年度・前期・数理解析・計算機数学2・第1回 2 【C (2)】 /******************************* * $Id: ex_01_01_1.c,v 1.3 2006-04-10 13:37:07+09 naito Exp $ * 浮動小数点演算の例(単精度浮動小数点) * 0.1 を 10 回加えても 1.0 にはならない *******************************/ #include <stdio.h> int main(int argc, char **argv) { float x = 0.1F, y = 0.0F ; int n ; for(n=0;n<10;n++) y += x ; printf("y = %f\n", y) ; if (y == 1.0F) printf ("y equals to 1.0\n") ; else printf ("y does not equals to 1.0\n") ; printf("y = %.8f\n", y) ; return 0 ; } 結果 y = 1.000000 y does not equals to 1.0 y = 1.00000012 注意事項 • 倍精度浮動小数点演算では 0.1 を 10 回加算しても 1.0 よりも小さな値となる. • 単精度浮動小数点演算では 0.1 を 10 回加算したら 1.0 よりも大きな値となる. ▼ サンプルプログラムのまとめ • 0.1 を 10 回加算しても 1.0 と等しくはならない. • この計算は, 「言語」や「実行する機器」には依存せず, 浮動小数点演算の精度のみに依存した結果 となる. (正確には「丸めモード」には依存している.) • 単精度浮動小数点演算で 0.1 を 10 回加算すると 1.0 よりもわずかに大きくなり, 倍精度浮動小数 点演算で 0.1 を 10 回加算すると 1.0 よりもわずかに小さくなる. ex01.tex,v 1.5 2008-04-14 18:00:24+09 naito Exp 2008年度・前期・数理解析・計算機数学2・第1回 3 ▼ いくつかの数値の計算 ここでは, 今後の授業のイントロダクションとして, いくつかの数値を簡単に(手抜きで)計算するプロ グラムを紹介する. これらの計算方法およびその問題点は, 今後の授業の中で解説する. ★ 2 の平方根の値の計算 【区間縮小法】 /* * SQRT[2] の値を近似的に求める. 方法1: 区間縮小法 */ #include <stdio.h> #include <math.h> #define N (20) int main(int argc, char **argv) { double left, right, sqrt2 ; int i ; left = 0.0 ; right = 2.0 ; for(i=0;i<N;i++) { sqrt2 = (left + right)/2.0 ; printf("%.15f\t%.15e\n", sqrt2, fabs(sqrt2-M_SQRT2)) ; if (sqrt2*sqrt2 <= 2.0) left = sqrt2 ; else right = sqrt2 ; } return 0 ; } 1.000000000000000 1.500000000000000 1.250000000000000 1.375000000000000 1.437500000000000 1.406250000000000 1.421875000000000 1.414062500000000 1.417968750000000 1.416015625000000 1.415039062500000 1.414550781250000 1.414306640625000 1.414184570312500 1.414245605468750 1.414215087890625 1.414199829101562 1.414207458496094 1.414211273193359 1.414213180541992 4.142135623730951e-01 8.578643762690485e-02 1.642135623730951e-01 3.921356237309515e-02 2.328643762690485e-02 7.963562373095145e-03 7.661437626904855e-03 1.510623730951455e-04 3.755187626904855e-03 1.802062626904855e-03 8.255001269048545e-04 3.372188769048545e-04 9.307825190485453e-05 2.899206059514547e-05 3.204309565485453e-05 1.525517529854525e-06 1.373327153264547e-05 6.103877001395475e-06 2.289179735770475e-06 3.818311029579746e-07 ex01.tex,v 1.5 2008-04-14 18:00:24+09 naito Exp 2008年度・前期・数理解析・計算機数学2・第1回 4 【ニュートン法】 /* * SQRT[2] の値を近似的に求める. 方法2: ニュートン法 */ #include <stdio.h> #include <math.h> #define N (8) int main(int argc, char **argv) { double sqrt2 ; int i ; sqrt2 = 2.0 ; for(i=0;i<N;i++) { printf("%.15f\t%.15e\n", sqrt2, fabs(sqrt2-M_SQRT2)) ; sqrt2 = sqrt2/2.0 + 1.0/sqrt2 ; } return 0 ; } 2.000000000000000 5.857864376269049e-01 1.500000000000000 1.416666666666667 8.578643762690485e-02 2.453104293571373e-03 1.414215686274510 1.414213562374690 2.123901414519125e-06 1.594724352571575e-12 1.414213562373095 2.220446049250313e-16 1.414213562373095 1.414213562373095 2.220446049250313e-16 2.220446049250313e-16 ex01.tex,v 1.5 2008-04-14 18:00:24+09 naito Exp 2008年度・前期・数理解析・計算機数学2・第1回 5 ★ 自然対数の底の値の計算 【定義をそのまま計算】 /* * 自然対数の底 $e$ の値を近似的に求める. 方法1の1: (1+1/n)^n の値を計算する. ひたす らかけ算する. */ #include <stdio.h> #include <math.h> double calc_e(unsigned long n) ; unsigned long n[] = { 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL, 1000000000UL, 10000000000UL, 100000000000UL } ; int main(int argc, char **argv) { int i, m ; double e ; m = sizeof(n)/sizeof(unsigned long) ; for(i=0;i<m;i++) { e = calc_e(n[i]) ; printf("%lu\t%.15f\t%.15e\n", n[i], e, fabs(e-M_E)) ; } return 0 ; } double calc_e(unsigned long n) { double x = 1.0 ; double y ; unsigned long int i ; y = 1.0 + 1.0/n ; for(i=0UL;i<n;i++) x *= y ; return x ; } 10 100 2.593742460100002 2.704813829421529 1.245393683590428e-01 1.346799903751572e-02 2.716923932235598 2.718145926824898 1.357896223446620e-03 1.359016341466734e-04 100000 2.718268237192295 1000000 2.718280469095936 1.359126675026801e-05 1.359363108743850e-06 1000 10000 10000000 2.718281694132010 1.343270348286296e-07 100000000 1000000000 2.718281798346360 2.718282052011900 3.011268523422928e-08 2.235528544503040e-07 10000000000 100000000000 2.718282053233160 2.718282053194587 2.247741144323356e-07 2.247355421758357e-07 ex01.tex,v 1.5 2008-04-14 18:00:24+09 naito Exp 2008年度・前期・数理解析・計算機数学2・第1回 6 【テイラー展開を利用する】 /* * 自然対数の底 $e$ の値を近似的に求める. 方法2の1: exp(x) のテイラー展開を計算する. */ #include <stdio.h> #include <math.h> unsigned long n[] = { 5UL, 10UL, 20UL, 30UL, } ; double calc_e(unsigned long n) ; int main(int argc, char **argv) { int i, m ; double e ; m = sizeof(n)/sizeof(unsigned long) ; for(i=0;i<m;i++) { e = calc_e(n[i]) ; printf("%lu\t%.15f\t%.15e\n", n[i], e, fabs(e-M_E)) ; } return 0 ; } double calc_e(unsigned long n) { double x = 1.0 ; double y = 1.0 ; unsigned long int i ; for(i=1UL;i<n;i++) { y *= 1.0/(double)i ; x += y ; } return x ; } 5 2.708333333333333 9.948495125712054e-03 10 20 2.718281525573192 2.718281828459046 3.028858528431044e-07 4.440892098500626e-16 30 2.718281828459046 4.440892098500626e-16 ex01.tex,v 1.5 2008-04-14 18:00:24+09 naito Exp 2008年度・前期・数理解析・計算機数学2・第1回 7 ★ 円周率の値の計算 【内接正多角形の長さを利用】 /* * 円周率 $pi$ の値を近似的に求める. 方法3の1: 多角形近似. 単なる倍角の公式. */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <math.h> #define N (20) unsigned long pi_montecalro(unsigned long n) ; int main(int argc, char **argv) { int i ; unsigned long n ; unsigned int p ; double pi ; double inner ; inner = 3.0 ; for(i=0;i<N;i++) { n = (1UL<<i)*6 ; printf("%lu\t%.15f\t%.15e\n", (1UL<<i)*6, inner, fabs(inner-M_PI)) ; inner = 2*n*sqrt((1.0 - sqrt(1.0-(inner/n)*(inner/n)))/2.0) ; } return 0 ; } 6 12 24 48 96 192 384 768 1536 3072 6144 12288 24576 49152 98304 196608 393216 786432 1572864 3145728 3.000000000000000 3.105828541230250 3.132628613281237 3.139350203046872 3.141031950890530 3.141452472285344 3.141557607911622 3.141583892148936 3.141590463236762 3.141592106043048 3.141592516588155 3.141592618640789 3.141592645321216 3.141592645321216 3.141592645321216 3.141592645321216 3.141593669849427 3.141592303811738 3.141608696224804 3.141586839655041 1.415926535897931e-01 3.576411235954335e-02 8.964040308556243e-03 2.242450542921048e-03 5.607026992633379e-04 1.401813044488165e-04 3.504567817103066e-05 8.761440857263381e-06 2.190353031394920e-06 5.475467448334825e-07 1.370016384782957e-07 3.494900369105380e-08 8.268577378345299e-09 8.268577378345299e-09 8.268577378345299e-09 8.268577378345299e-09 1.016259633779271e-06 3.497780554084784e-07 1.604263501064906e-05 5.813934751852656e-06 ex01.tex,v 1.5 2008-04-14 18:00:24+09 naito Exp 2008年度・前期・数理解析・計算機数学2・第1回 8 【テイラー展開を利用する】 /* * 円周率 $pi$ の値を近似的に求める. 方法1の1: arctan(x) のテイラー展開を x = 1 で求 める. */ #include <stdio.h> #include <math.h> unsigned long n[] = { 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL } ; double atan_taylor(unsigned long n, double x) ; int main(int argc, char **argv) { int i, m ; double pi ; m = sizeof(n)/sizeof(unsigned long) ; for(i=0;i<m;i++) { pi = atan_taylor(n[i], 1.0)*4.0 ; printf("%lu\t%.15f\t%.15e\n", n[i], pi, fabs(pi-M_PI)) ; } return 0 ; } double atan_taylor(unsigned long n, double x) { double v = 0.0 ; double y ; unsigned long int i ; int sign = 1 ; y = x ; for(i=1UL;i<n;i+=2) { if (sign) v += (1.0/(double)i) * y ; else v -= (1.0/(double)i) * y ; y *= x*x ; sign ^= 1 ; } return v ; } 10 100 3.339682539682540 3.121594652591011 1.980898860927471e-01 1.999800099878213e-02 1000 10000 3.139592655589785 3.141392653591791 1.999998000008052e-03 1.999999980020206e-04 100000 3.141572653589781 2.000000001167734e-05 1000000 3.141590653589692 2.000000101087807e-06 3.141592453589780 2.000000134394497e-07 10000000 ex01.tex,v 1.5 2008-04-14 18:00:24+09 naito Exp 2008年度・前期・数理解析・計算機数学2・第1回 9 【注意0】 情報メディア教育センターの MacOSX では, C コンパイラ利用時には -m64 オプションをつ けると, long, unsigned long が 8 バイトとなります. gcc -m64 foo.c とする. (-m64 がついていることに注意.) 【注意1】 C において pow などの「数学関数ライブラリ」を用いる場合には, gcc foo.c -lm または gcc -m64 foo.c -lm とする. (-lm がついていることに注意.)代表的な数学関数は以下の通り. acos acosh arc cosine function inverse hyperbolic cosine function asin asinh arc sine function hyperbolic sine function atan arc tangent function atanh ceil inverse hyperbolic tangent function ceiling value function cos cosh cosine function hyperbolic cosine function exp fabs exponential function absolute value function floor log floor function natural logarithm function pow sin power function sine function sinh sqrt hyperbolic sine function square root function tan tanh tangent function hyperbolic tangent function 【注意2】 math.h の中では, 以下の数学定数の値が浮動小数点定数として定義されている. M_E M_LOG2E The base of natural logarithms (e). The base-2 logarithm of e. M_LOG10E M_LN2 The base-10 logarithm of e. The natural logarithm of 2. M_LN10 The natural logarithm of 10. ex01.tex,v 1.5 2008-04-14 18:00:24+09 naito Exp 2008年度・前期・数理解析・計算機数学2・第1回 10 M_PI pi, the ratio of the circumference of a circle to its diameter. M_PI_2 M_PI_4 pi/2. pi/4. M_1_PI M_2_PI 1/pi. 2/pi. M_2_SQRTPI 2 over the square root of pi. M_SQRT2 The positive square root of 2. M_SQRT1_2 The positive square root of 1/2. 【注意3】 この授業の実習・課題やレポートの回答では, 「IEEE754 浮動小数点演算標準」に準拠してい る言語であれば, 何を使っても構わないこととする. (C, Java, Fortran, OCaml などはこれに該当す る.) ex01.tex,v 1.5 2008-04-14 18:00:24+09 naito Exp
© Copyright 2024 Paperzz