• ベストアンサー

数値計算で生じる小さなごみ

ある問題を数値的に解こうと思いFortranでプログラムを組んでいるのですが、倍精度の数値計算で以下のような事が起きて困っています。 プログラムの中で A=B-C*D (変数は全て倍精度実数変数) のような代入文があるのですが、 write(*,*) B,C,D とすると、0.250000000000000 0.500000000000000 0.500000000000000 と表示されるのですが、 write(*,*) B-C*D や write(*,*)A では-1.79570984817912D-017 などと表示されます。(この数値に意味はないのでしょうが、同じシチュエーションでは常に同じ値が表示されます) 入力した値の精度外の「誤差」ですが、どこに原因のある問題でしょうか? 因に使っているコンパイラはintelのサイトで入手したフリーの評価版ifc ver.6 でそれにpentium4に最適化するオプションをつけてコンパイルしています

質問者が選んだベストアンサー

  • ベストアンサー
noname#11476
noname#11476
回答No.3

他に間違っている部分がなくて、その通りとするとおかしいですね。 本当にB,C,Dに0.2,0.5の数字が入っていてそうなるのであればコンパイラのバグかもしれませんが、オーダーが大きすぎるし、pen4が実際には計算している筈なので余り考えられません。 一応、最適化オプションをはずして試しても同じですか? あと、単精度ではどうですか? 0.25, 0.5 という数字は2進数では割り切れる数値なのでそのようなことが起きること自体が不思議です。 気になるのは一見0.25とか0.5に見えても、何らかの計算の結果の数値とすればそのような現象になるのは至極当然のことです。 というのも、表示するときには必ず丸められて表示されるので、0.2とか0.5に対してD-17だと完全に丸められる領域だからです。 差分をとったときにはもちろんクローズアップするのでわかります。 試しに、 0.5 - 0.5 が 0になることを確認して、 C - 0.5 を表示させるなどすれば、Dが本当に0.5なのかどうかがわかります。 もう一つは、一度単精度の変数に代入して、また倍精度の変数にいれるとか、あるいは関数を使って下10桁で四捨五入処理するなどですね。 この手の話は良くあることです。

toro2000
質問者

お礼

ありがとうございました。 >試しに、 0.5 - 0.5 が 0になることを確認して、 C-0.5 >を表示させるなどすれば、Dが本当に0.5なのかどうかがわかります。 教えていただいたことを実行してみたところ、0.25に関してゴミがそのままでてきました。 なるほどこういうことがあるんですね。勉強になりました。 >この手の話は良くあることです。 のようですね。うっかり周りにいた詳しい人にも尋ねた所、大笑いされてしまいました。 どうもお世話になりました。

その他の回答 (3)

noname#21649
noname#21649
回答No.4

一つの可能性は. B=0.25 のように指定すると.Bは倍精度.0.25は単精度と解釈して.0.250000(中略)123...なんて数値が代入されている可能性があります。 0.25d1 のような倍精度を指定して代入式を作ってみてください。 これが失敗した場合には.全部整数で代入式を作り演算によって倍精度にする方法があります。 インテルの場合.たしか.有効桁以下は0以外の数値が入っている場合が多いので.これが変な数値の原因となっていると考えられます。

toro2000
質問者

お礼

ありがとうございます >0.25d1 >のような倍精度を指定して代入式を作ってみてください。 ご指摘の通り代入式もいい加減でした。レベルがばれそうで恥ずかしいです。全部書き直しました。直接の原因ではなかったみたいですがこれから気をつけることにします。 >インテルの場合.たしか.有効桁以下は0以外の数値が入っている場合が多いので.これが変な数値の原因となっていると考えられます。 確かにそうでした。これからは気をつけたいです。 みなさんありがとうございました。この場をかりてお礼申し上げます。

  • aton
  • ベストアンサー率47% (160/334)
回答No.2

同じくFortranはよく知らないのですが…。 お使いになっているFortranではBCD (Binary Coded Decimal, 二進化十進数→参考URL) 型の実数は扱えないのでしょうか? これが使えれば,誤差の問題は解決出来るかもしれません。

参考URL:
http://www.ffortune.net/comp/develop/data/kazu.htm
toro2000
質問者

お礼

ありがとうございました。 はずかしいことに二進化十進数は初耳でした。でも確かにこうしないとだめですよね。私のもっているfortranの教科書には実数は単精度、倍精度などの分類はありますが内部での表現の指定方法はないです。無いのかもしれないです。 他のfortranのコンパイラでは問題の無いプログラムなんですが。

noname#202350
noname#202350
回答No.1

Fortranは分からないので恐らくなのですが・・・。 コンピュータで計算を行う場合、大体に置いて2進数で計算を行います。 ところが、小数点を含む計算を行う場合に問題が出てくるのです。 というのは、10進数での特定の数字が2進数では表せないのです。 (詳しい事は昔教えてもらったのですが忘れてしまいました。) そのため、2進数に変換が出来ないので2進数の近似値で計算を行います。 これによって誤差が生じてくるのです。 一般的に通貨型とか言われているような型でしたら誤差が出ないと思います。 たとえばVisualBasicではCurrency型がそれにあたります。

toro2000
質問者

お礼

御返事ありがとうございました。 なにも考えないで計算させてましたが中の事も考えないとだめですよね。 いろいろ考えてみましたが、2進数で循環小数になる場合などは確かに有限桁で切ると誤差の原因になりそうです。 あと、質問には書き忘れましたが、write(*,*)0.25-0.5*0.5は正確に0となります。変数に値が入っている時だけなのです。

関連するQ&A

  • 計算精度(限りなくゼロに近く)

    現在、fortranで大きな数値計算をしています。 そこで、計算回数が多く、その計算過程において大きい値と小さい値とを何回も計算していると誤差が次第に大きくなっていきます。 A=BとなるようにA(又はB)を求めるときの判別としてabs(A-B)<10**(x) としています。 このxに-300ぐらいの値をいれ判別したいのですがそれは可能ですか? もちろん計算には倍精度を使っていますがabs(A-B)が10**(-17)付近になると値が一定になってしまいます。これがfortranの限界なのでしょうか?

  • fortranでx=1としても0.9..が入る

    fortranで以下のようなプログラムにてxに1.0^-6を代入し、 その中身を表示しました。 --------------------------------- program test real(8) :: x = 1.0d-6 write(*,*) x write(*,*) x * 1.0d6 write(*,*) int(x * 1.0d6) end program test --------------------------------- しかし、結果は以下のようになり、 0.999999999999999955^-6となってしまいます。 さらに、やっかいなことに10^6倍して 整数型に変換しても0と認識されてしまうのです。 ---------------------------------- $ ./a.exe 9.99999999999999955E-007 1.00000000000000000 0 ---------------------------------- 変数の型も倍精度で宣言し、定数も倍精度(d付き)で 代入しているはずなのですが、なぜこのような現象が 起きるのでしょうか。 ご存知の方いましたら教えて頂けると助かります。 なお、コンパイルはgfortranで行っています。

  • Fortranの変数の配列の設定値を計算中に設定するには?

    FortranでプログラムをつくるときAという変数にBの値の範囲で配列を設定する。A(B) このとき、プログラム作成時にはAには具体的な数値を設定しておかなくても良いですが、Bには具体的に配列の数を設定しておく必要があるように思います。  質問は、  プログラムが動く初めにはBに具体的な値を入れず、プログラム作動状況に応じてプログラム作動中にBの配列の範囲を設定できないものでしょうか?

  • あるセル内の計算結果を固定数値として違うセルに代入する方法

    あるセルで計算した結果を違うセルの位置に固定数値として代入する方法はあるのでしょうか? 例えば、A1のセルに変数Xが入っており、B1のセルに変数Yが入っていたとします。 そしてC1のセルで、ある計算をしていたとします。 で、X=Yの条件を満たした時はC1のセルの計算結果の数値をセルD1に数値として自動で代入するという作業をやらせたいのです。 試しに適当なセルに、=IF(A1=B1,D1=C1,0)なんて式を作ってみましたが、当然のようにD1=C1なんて表現はできないのでエラーとなります。 D1のセルに=IF(A1=B1,C1,0)と入れれば、A1=B1の条件を満たした時だけはD1のセルにC1の計算結果が入りますが、D1のセル内が数値でなくあくまで条件式である以上、条件が変わればエクセル上のD1のセルの表示も変わってしまいます。あくまでC1の計算結果の数値をそのままD1に数値のデータとして入力させて記憶させておきたいのですが。 どうやったらいいのでしょうか? D1のセルに=C1と入れておけば、D1のセルにはC1の計算結果が常に表示されるわけですが、C1の計算自体が他のセルのデータによってその都度変動するため、D1も同じように変動してしまいます。 ですので、A1=B1を満たした時のC1の計算結果をD1に記憶させておくという作業をしたいわけなんです。

  • エクセルでの一括計算方法がありましたら教えてください

    A2~A60の1列に数値が入っています。 その値すべてに 0.55を掛けた値を隣のセル B2~B60に表示したいのですが 一括でできる方法はあるでしょうか? 最終的には、B2~B60の数値に隣のセルC2~C60の値を掛けた数値をD2~D60に表示したいのですが。。 1セルごとに関数を入れる以外に方法があれば と思って質問しました。 ご存知の方がいらっしゃいましたら ご指導 よろしくお願いします<(__*)>

  • C言語の不等号の計算

    三つの整数の変数a,b,cを用意して、プログラムの各変数に任意の値を代入する 例:a=9,b=31,c=25 このプログラムを実行して 「a=9,b=31,c=25です.よって b>c>a です.」 と表示するプログラムを組みなさい とゆう課題が出されたのですが switch文を使ってプログラムを組んで課題は終わったのですが・・・ switch文以外方法でこのプログラムが組めるのか プログラムの例を教えてください。 あとプログラムを組むときにはシンプルで行数の少ないプログラムのほうがいいのですか? 教えてくださいお願いします。

  • 数値計算ができるようになりたいのですが…

    現在、大学院修士課程1年です。 将来はアカデミックな研究者を目指しています。 今行っている研究で、非線形方程式(多変数)の解を数値計算を用いて得たいと考えています。 今まで数値計算はやったことがないのでこれから勉強をはじめるところなのですが、少し悩んでいます。 今は数値計算に便利なMATLABのような市販ソフトがあることが分かっており、勉強すれば短時間で実際に研究につかうことができそうです。 しかし、やはり研究者を目指すなら、そのようなソフトに頼らず、しっかりと数値計算の理論を理解したうえで、FORTRANなどで自らプログラムを書くべきなのでしょうか。 良いアドバイスがありましたらよろしくお願いします。

  • UNIX フォートラン 数値計算精度

    フォートランでの数値計算精度に関して困っております。 サンマイクロシステムズ社製、UNIX、Solaris10(64bit)において、 下のフォートランプログラム、 IMPLICIT REAL*8(A-H,O-Z) X=1.0D0 A=SIN(X) WRITE(*,100)A 100 FORMAT(F50.40) STOP END を実行させると、 A=0.8414709848078965048756572286947630345821 となり、16桁以降にも数値が出てきます。 これはなぜでしょうか? UNIXコンパイラの特徴なのでしょうか?

  • 入力した数値になるよう組み合わせを計算したい

    A.5~500までの数値を入力 B.1~100までの数値のうち、5つの数値を組み合わせ、Aで入力した値と同様の値となるようにする という計算を行うプログラムを組もうとしているのですが、どういった計算を行えばよいのか分かりません。 ご存知の方いらっしゃいましたらアドバイスお願いします。 ちなみにこれは学校の課題ではありません。個人的な興味です。

  • Excelで同数値の数を数える

    次のような数値があるとします。  A   B   C   D  201   1   205   2           201   2   205   1   203   1  行A、Cには201、202などの値がランダムに 表示されています。(関数で表示された値です) 行Bには、例えばB1にはA1:A3の範囲中にA1セルで表示されて いる値と同じ値がいくつあるかをカウントしてあるのですが、 このカウントに関数を使いたいのです。 行Dには、A1:C3の範囲で同じようにカウントした結果を表示 させたいと思います。 同じ行に同じ値が表示されていることはありません。 AとCのセルが空白の場合はBとDも空白にしたいため、 IF(A1="","",COUNTIF($A$1:$A$3,A1)) と入力してみたのですが、空白セルに関数があるせいか、 空白セルまでカウントされてしまいます。 行Aの関数を消すとうまくいくのですが、 消さずに、空白セルをカウントしない ようにする方法はあるでしょうか。 お返事お待ちしています。  

専門家に質問してみよう