• 検索結果がありません。

浮動小数点演算の例(倍精度浮動小数点

N/A
N/A
Protected

Academic year: 2021

シェア "浮動小数点演算の例(倍精度浮動小数点"

Copied!
11
0
0

読み込み中.... (全文を見る)

全文

(1)

● 講義資料

▼ サンプルプログラム

計算機内部の浮動小数演算では,通常とは違った計算になることがある. この例では0.110回加えて 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を書

(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よりも大きな値となる.

(3)

【Java】

//

// $Id: ex_01_01.java,v 1.2 2006-04-05 16:38:59+09 naito Exp $ //

import java.io.* ;

/**

* 浮動小数点演算の例: 0.1 10 回加えても 1.0 にはならない

* @author Hisashi Naito

*/

public class ex_01_01 {

public static void main (String args[]) throws IOException { double x = 0.1, y = 0.0 ;

int n ;

for(n=0;n<10;n++) y += x ; System.out.println("y = " + y) ;

if (y == 1.0) System.out.println("y equals to 1.0") ; else System.out.println("y does not equals to 1.0") ; }

}

コンパイル・実行方法

javac ex_01_01.java java ex_01_01

結果

y = 0.9999999999999999 y does not equals to 1.0

注意事項

doubleは「倍精度浮動小数点型」である.

「単精度浮動小数点型」は floatである.

「浮動小数点定数」0.1,1.0E-1などの表記はdouble型定数であり,float型定数として 0.1を書 くときには0.1F,1.0E-1Fなど書く.

結果(単精度浮動小数点演算)

y = 1.0000001

y does not equals to 1.0

(4)

【Fortran】

C $Id: ex_01_01.f,v 1.1 2006-04-10 21:17:19+09 naito Exp $ C 浮動小数点演算の例: 0.1 10 回加えても 1.0 にはならない

PROGRAM EX_01_01 REAL X, Y INTEGER N

X = 0.1 ; Y = 0.0 ; DO N=1,10,1

Y = Y + X ENDO

WRITE(*,601) ’Y = ’, Y IF (Y .EQ. 1.0) THEN

PRINT *, ’Y EQUALS TO 1.0’

ELSE

PRINT *, ’Y DOES NOT EQUALS TO 1.0’

ENDIF

601 FORMAT(A,F10.8) STOP

END

コンパイル・実行方法

g77 ex_01_01.f ./ex_01_01

結果

y = 1.00000012

y does not equals to 1.0

注意事項

realは「単精度浮動小数点型」である.

「倍精度浮動小数点型」は double precisionである.

「浮動小数点定数」0.1,1.0E-1などの表記は real型定数であり,double precision型定数とし 0.1 を書くときには0.1D0,1.0D-1など書く.

結果(倍精度浮動小数点演算)

y = 0.9999999999999999 y does not equals to 1.0

(5)

【OCaml】

(* ex_01_01.ml *)

(* $Id: ex_01_01.ml,v 1.3 2006-04-05 18:57:54+09 naito Exp $ *) (* 浮動小数点演算の例: 0.1 10 回加えても 1.0 にはならない *)

let ex_01_01 () =

let x = 0.1 and y = ref 0.0 in

for n = 1 to 10 do y := x +. !y done ; Printf.printf "%f\n" !y ;

if (!y == 1.0) then Printf.printf "y equals to 1.0\n"

else Printf.printf "y does not equals to 1.0\n";

Printf.printf "%.16f\n" !y ;

;;

ex_01_01() ;;

コンパイル・実行方法

ocamlc ex_01_01.ml

ocamlc ex_01_01.cmo -o ./ex_01_01 ./ex_01_01

結果

1.000000

y does not equals to 1.0 0.9999999999999999

注意事項

OCamlにおけるfloatは「倍精度浮動小数点型」である.

「単精度浮動小数点型」は OCamlには存在しない.

(6)

▼ サンプルプログラムのまとめ

どんな言語でも0.110回加算しても1.0と等しくはならない.

この計算は, 「言語」や「実行する機器」には依存せず,浮動小数点演算の精度のみに依存した結果 となる. (正確には「丸めモード」には依存している.)

単精度浮動小数点演算で 0.110 回加算すると1.0よりもわずかに大きくなり, 倍精度浮動小数 点演算で 0.1 10回加算すると1.0よりもわずかに小さくなる.

(7)

▼ 計算例

次のページの表はいくつかの方法を利用して lim

n→∞(1 + 1/n)n を計算しようとしたものである.

なお,よく知られている通り lim

n→∞(1 + 1/n)n=eであり,eの小数点以下50桁までの真の値は, 2.71828182845904523536028747135266249775724709369995

である.

n 方法1 方法2 方法3

1 2.0000000000 2.0000000000000000 2.0000000000000000 5 2.4883201122 2.4883199999999999 2.4883199999999994 10 2.5937430859 2.5937424601000019 2.5937424601000023 50 2.6915805340 2.6915880290736047 2.6915880290736078 100 2.7048130035 2.7048138294215289 2.7048138294215285 500 2.7155508995 2.7155685206516980 2.7155685206517282 1000 2.7170419693 2.7169239322355203 2.7169239322355936 5000 2.7180233002 2.7180100501016717 2.7180100501015549 10000 2.7180233002 2.7181459268243562 2.7181459268249255 50000 2.7215235233 2.7182546461335386 2.7182546461267321 100000 2.7215235233 2.7182682371975284 2.7182682371922975 500000 2.7544298172 2.7182791102198656 2.7182791102603661 1000000 2.5946049690 2.7182804691564275 2.7182804690957534 5000000 3.2929797173 2.7182815551595598 2.7182815552001292 1.0e+07 3.2929797173 2.7182816939803724 2.7182816941320818 5.0e+07 1.0000000000 2.7182818165706681 2.7182818149349388 1.0e+08 2.7182817863957975 2.7182817983473577 5.0e+08 2.7182817290321108 2.7182817488625042 1.0e+09 2.7182820308145095 2.7182820520115603 5.0e+09 2.7182820434752482 2.7182820530988732 1.0e+10 2.7182820434752482 2.7182820532347876 5.0e+10 2.7182820301160255 2.7182820533435188 1.0e+11 2.7182820301160255 2.7182820533571102 5.0e+11 2.7182216686540404 2.7182216960516250 1.0e+12 2.7185234695682796 2.7185234960372378 5.0e+12 2.7191271816740366 2.7191271965357213 1.0e+13 2.7161100174314221 2.7161100340869009 5.0e+13 2.7161100084852716 2.7161100340870092 1.0e+14 2.7161100084852716 2.7161100340870226 5.0e+14 2.7161100048574176 2.7161100340870337 1.0e+15 3.0350351813962924 3.0350352065492618 5.0e+15 3.0350351814145453 3.0350352065492636 1.0e+16 1.0000000000000000 1.0000000000000000 てなわけで,プログラムを組んで調べてみると

n→∞lim(1 + 1/n)n = 1

であることがわかる. あれぇ?

方法1 演算精度:単精度浮動小数点演算(float),乗算方法:高速乗算法 方法2 演算精度:倍精度浮動小数点演算(double),乗算方法:高速乗算法 方法3 演算精度:倍精度浮動小数点演算(double),乗算方法:pow関数

(8)

各方法について,|e−(1 + 1/n)n|をプロットしたもの

1e-09 1e-08 1e-07 1e-06 1e-05 0.0001 0.001 0.01 0.1 1 10

1 100 10000 1e+06 1e+08 1e+10 1e+12 1e+14 1e+16

Method 2

1e-09 1e-08 1e-07 1e-06 1e-05 0.0001 0.001 0.01 0.1 1 10

1 100 10000 1e+06 1e+08 1e+10 1e+12 1e+14 1e+16

Method 2

方法1 方法2

1e-09 1e-08 1e-07 1e-06 1e-05 0.0001 0.001 0.01 0.1 1 10

1 100 10000 1e+06 1e+08 1e+10 1e+12 1e+14 1e+16

Method 3

0 1 2 3 4 5

1 100 10000 1e+06 1e+08 1e+10 1e+12 1e+14 1e+16

方法3

単精度及び倍精度の方法について,誤差の評価直線を追加したもの

1e-09 1e-08 1e-07 1e-06 1e-05 0.0001 0.001 0.01 0.1 1 10

1 100 10000 1e+06 1e+08 1e+10 1e+12 1e+14 1e+16

1e-09 1e-08 1e-07 1e-06 1e-05 0.0001 0.001 0.01 0.1 1 10

1 100 10000 1e+06 1e+08 1e+10 1e+12 1e+14 1e+16

単精度 倍精度

(9)

★ 方法2のプログラム(Solaris 用)

/* sample_01_2.c

* (1+1/n)^n を計算する

* 倍精度浮動小数点演算

*/

#include <stdio.h>

double power(double, unsigned long) ; unsigned long print_value[] = {

1, 5, 10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000, 500000, 1000000, 5000000, 10000000, 50000000, 100000000, 500000000, 1000000000, 5000000000, 10000000000, 50000000000, 100000000000, 500000000000, 1000000000000, 5000000000000, 10000000000000, 50000000000000, 100000000000000, 500000000000000, 1000000000000000, 5000000000000000, 10000000000000000, 50000000000000000 } ;

int main(int argc, char **argv) {

int i ;

int limit = sizeof(print_value)/sizeof(print_value[0]) ; for(i=0;i<limit;i++) {

fprintf(stdout, "%-16lu\t%18.16f\n", print_value[i],

power((1.0+1.0/(double)print_value[i]), print_value[i])) ; }

return 0 ; }

double power(double x, unsigned long n) {

double x0 = 1.0, x1 = x ; while(n) {

if (n&1) x0 *= x1 ; x1 *= x1 ;

n >>= 1 ; }

return x0 ; }

(10)

★ 方法2のプログラム(Linux 用)

/* sample_01_2.c

* (1+1/n)^n を計算する

* 倍精度浮動小数点演算

*/

#include <stdio.h>

double power(double, unsigned long long) ; unsigned long long print_value[] = {

1, 5, 10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000, 500000, 1000000, 5000000, 10000000, 50000000, 100000000, 500000000, 1000000000, 5000000000, 10000000000, 50000000000, 100000000000, 500000000000, 1000000000000, 5000000000000, 10000000000000, 50000000000000, 100000000000000, 500000000000000, 1000000000000000, 5000000000000000, 10000000000000000, 50000000000000000 } ;

int main(int argc, char **argv) {

int i ;

int limit = sizeof(print_value)/sizeof(print_value[0]) ; for(i=0;i<limit;i++) {

fprintf(stdout, "%-16lu\t%18.16f\n", print_value[i],

power((1.0+1.0/(double)print_value[i]), print_value[i])) ; }

return 0 ; }

double power(double x, unsigned long long n) {

double x0 = 1.0, x1 = x ; while(n) {

if (n&1) x0 *= x1 ; x1 *= x1 ;

n >>= 1 ; }

return x0 ; }

(11)

● 実習内容

1.010回加算したら,その結果は10.0と等しくなるかを調べなさい. また,その理由はなぜかを 考察しなさい.

lim

n→∞(1 + 1/n)n を計算するために,nを上の表のように動かして, (1 + 1/n)n の値を出力するプログ ラムを書き,得られたデータから前ページのようなグラフをgnuplotを用いて書きなさい. 乗算の方 法としては「高速乗算法」を用いなさい. また,このような結果となる理由を考察しなさい.

なお, 大学院生が利用する Solaris上では unsigned long 8 バイトであるが, 学部生が利用する Linux上では4バイトである. Linux上ではunsigned long long 8バイト整数である.

lim

n→∞n1/n lim

x→0sin(x)/x は, 上記のような方法で正しく計算できるかを考えなさい. また, その理 由を考察しなさい.

【注意1】 Cにおいて powなどの「数学関数ライブラリ」を用いる場合には,

gcc foo.c -lm

とする. (-lmがついていることに注意.)

【注意2】 この授業の実習・課題やレポートの回答では,「IEEE754浮動小数点演算標準」に準拠してい る言語であれば,何を使っても構わないこととする. (上記のC, Java, Fortran, OCamlなどはこれ に該当する.)

参照

関連したドキュメント

Mochizuki, Topics in Absolute Anabelian Geometry III: Global Reconstruction Algorithms, RIMS Preprint 1626 (March 2008)..

Yamamoto: “Numerical verification of solutions for nonlinear elliptic problems using L^{\infty} residual method Journal of Mathematical Analysis and Applications, vol.

[r]

事業セグメントごとの資本コスト(WACC)を算定するためには、BS を作成後、まず株

⑥ニューマチックケーソン 職種 設計計画 設計計算 設計図 数量計算 照査 報告書作成 合計.. 設計計画 設計計算 設計図 数量計算

【資料1】最終エネルギー消費及び温室効果ガス排出量の算定方法(概要)

令和元年度予備費交付額 267億円 令和2年度第1次補正予算額 359億円 令和2年度第2次補正予算額 2,048億円 令和2年度第3次補正予算額 4,199億円 令和2年度予備費(

項   目  単 位  桁   数  底辺及び垂線長 m 小数点以下3桁 境界辺長 m  小数点以下3桁