浮動小数演算での誤差の蓄積が起こっているのかどうか

このQ&Aのポイント
  • C++でプログラミングの勉強をしています。ある実数演算(double)をする関数A(実数パラメータxを用いた積算)を2種類の方法で用いると、結果が微妙に変わってしまいます。条件は同じなので、同じ結果が出て欲しいのですが、結果が変わってしまい困っています。原因としては何が考えられるでしょうか?
  • ループ中に共有している変数は必ず初期化しているのに、前のパラメータの時の実数演算の誤差が積み重なっている可能性はありますか?ソースコードは複雑なため、載せることはできません。原因として考えられることを教えて頂けると助かります。
  • 悩んでいる現象は、パラメータxを微小に変化させながら関数Aをfor文でループさせ、最後の結果を入手する方法と、パラメータxの大きさを同じにして関数Aを1回のみ用いて結果を入手する方法で、微妙に結果が変わる点です。条件は同じだと思うので、なぜ結果が異なるのかがわかりません。どんなことが考えられる原因でしょうか?
回答を見る
  • ベストアンサー

浮動小数演算での誤差の蓄積が起こっているのかどうか

C++でプログラミングの勉強をしています. 以下の現象で悩んでいます. ある実数演算(double)をする関数A(実数パラメータxを用いた積算)を 以下の2種類の方法で用いると,結果が微妙に変わってしまいます. (1)パラメータxを微小に変化させながら,関数Aをfor文でループさせ,最後の結果を入手する.  (xを変化させても,Aの結果が変わらないようにその他の変数を初期化しているのですが・・・) (2) (1)の最終条件(パラメータxの大きさ)を同じにして,関数Aを1回のみ用いて,結果を入手する. 条件は同じはずなので,同じ結果が出て欲しいのですが, 結果が変わってしまい困っています. この場合考えられる原因としては何があるでしょうか? ・前のパラメータの時の実数演算の誤差が積み重なっている可能性はありますか? (でも,ループ中に共有している変数は必ず初期化しているのですが・・・) ソースコードは複雑なため,載せることはできません. 大変申し訳ございません. 原因としてどんなことが考えられるのかを教えて頂けると助かります. 宜しくお願い致します.

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

  • ベストアンサー
回答No.1

パラメータをたとえば0→1の間で10000分割した場合、 forループで1万回0.0001を加算して最後に1になった場合 の結果を出していれば、誤差で一致しない可能性があると思います。 0.0001×10000でも誤差が出るでしょう。 今思いついたのはその位です。

tokkotokon
質問者

お礼

無事同じ結果が出ました. やはり,実数を何回も足していたのが原因でした. 私の些細なミスに対して,時間を割いてまで回答して頂きありがとうございました. doradora55さんの欄にて回答して頂いた皆様にお礼を申し上げます.

tokkotokon
質問者

補足

回答の通りかもしれません. (1)はパラメータxを0.01刻みでループさせています. 例えば 「xを0.25まで加算させた際の結果」と 「xに0.25を代入した際の結果」では違う値になっている可能性があります. それならば,ループの際に0.01加算するのではなく, 「x(整数型にしておく)に1加算して,100で割る」 という方法で対処できますか? つまり,0.01ずつ加算して0.25を表現する(A式)のではなく, B式でやれば誤差は生じないでしょうか? A式:y(実数) = x(実数) + 0.01で B式:y(実数) = ( x(整数) + 1 ) / 100

その他の回答 (4)

  • ki073
  • ベストアンサー率77% (491/634)
回答No.5

シミュレーションソフトを動かしていて計算式自体が同じソースコードなのに結果が微妙に違うことがあります。 今把握している(想像している?)原因としては (1) 同じソースコードなのに、ループの中などに存在すると、コンパイラの最適化で式の順序が入れ替えられ、数学の式としては等価ですが、小数点誤差の蓄積され方が異なる。 (2) 割り算を高速化するために、逆数に変換してからかけ算をするように最適化されることもあります。直接割り算よりも速いですが、誤差が大きくなります。 (3) コンパイルするときにIEEE 754に完全には準拠していない演算になっていることが多いです。丸め誤差などが微妙に違うようです。 コンパイラの最適化なし(オプション-O0が多いです)でやってみたらどうですか?(1)と(2)はこれで避けられるはず。 (3)は別にオプションがあるかもしれませんし、-O0でIEEE754に準拠されるかは分かりません。私が使っているPGIコンパイラは別にあります。 普通の計算では目立たないのですが、このようなことがあると何万回も加算をすると差が出ます。

  • kmee
  • ベストアンサー率55% (1857/3366)
回答No.4

0,01を25回足すのは、毎回、真の0.01との誤差が加わるので、誤差が蓄積されやすいです。 大使、25 * 0.01だと、真の0.01と0.01との誤差はこの時点では最小だし、かけ算は一時的により大きな桁で計算して結果をまるめる形になるので、誤差は小さくてすみます。 有限桁の2進浮動小数点を使った数値演算ではいろんなところで誤差が出るので、精度よく計算したいなら常に考えておく必要があります。

  • wormhole
  • ベストアンサー率28% (1621/5656)
回答No.3

最終的には同じ結果になるはずのものでも 浮動小数点演算があってその手順が異なるのであれば 演算誤差に差異があるのは当然かと思いますが。 そういう場合は許容誤差いくらかまでは同じとみなすとかするんじゃないかなぁ。 doubleやfloatが保持できるのは近似値というのは理解されていますよね?

tokkotokon
質問者

補足

近似値になると言うのはもちろん理解しています. ただ,(1)は独立した関数Aをループさせているはずなので, 演算誤差も同じ値が出るのではないのですか? ループ内の関数Aの演算が次のパラメータの際に影響するなら, 演算誤差は違う値になるのではないのかと思います. 違っていたら申し訳ございません.

  • ki073
  • ベストアンサー率77% (491/634)
回答No.2

整数は2進数で正確に表せるが、小数部分は正確に表せないものがあるからです。 例えば、0.1は2進数でどう表すか調べてみてください。それを10倍してみてください。 浮動小数点をイコールで比較すると問題がでるのもそのためです。

関連するQ&A

  • 文字列連結演算子と浮動小数点型

    本に 「文字列型連結演算子では浮動小数点型等の出力形式を指定できません。出力をフォーマットしたい場合はprintf関数を利用する」 と記載されていますが、これ以上の詳しい説明が記載されていないので、それがちょっと分かりません。分かる方は教えてください。 私の仮定では、 --------------------------------------------- <?php $a=1.23; $b=1.23; print $a.$b //文字列連結演算子で$aと$bを繋ぐ。 ?> --------------------------------------------- 出力結果は1.231.23となる。それを回避するために --------------------------------------------- <?php $a=1.23; $b=1.23; printf("%f",$a.$b) //printf関数を使って出力をフォーマット ?> --------------------------------------------- 出力結果は1.231000となりますが、その解釈で正しいでしょうか。

    • ベストアンサー
    • PHP
  • 見えない浮動小数点演算誤差?

    二つのBOOKにある表のデータの数値をVBAで比較していました。 単に各セルのValueとValueが等価かどうか=で比較しただけです。 すると見た目(表示)も数式バー上の値もまったく同じなのに相違があると判定されました。 不思議に思い、二つのBOOKにある表のデータの数値のうち違いがあると出た2つのセルを、そのまま別シートにコピー貼り付けして比較したのが添付の図です。 たとえば、BOOK-AからコピペしたB3セルの値は0.669です。 BOOK-BからコピペしたC3セルの値も0.669です。 両方とも数式バーでみましたが、間違いなく0.669です。 A3セルに =B3=C3 と入れるとTRUEが返ります。 ところが、D3セルに =B3-C3=0 と入れるとFALSEが返ります。 では、0でないなら差額はいくら?と、B11セルに=B3-C3と入れると、0が返ります。 これまで浮動小数点演算誤差で、見た目がおなじでも小数点以下かなり下の方で違いケースは経験していましたが、その場合でも小数点以下の表示を20位くらいまで表示させると違いが表れました。ところが今回は誤差が見えません。 差額確認のためVBAで Sub test01() Debug.Print Range("B3").Value = Range("C3").Value Debug.Print Range("B3").Value - Range("C3").Value Debug.Print Range("B5").Value = Range("C5").Value Debug.Print Range("B5").Value - Range("C5").Value End Sub としてみると、 False 1.11022302462516E-16 False 1.11022302462516E-16 が返りました。 エクセル2010と2016の2つで試しましたが同じ結果でした。 こんなことってあるんでしょうか? 困惑しています。

  • perl 条件演算子 範囲演算子についてです

    演算子 条件演算子 条件演算子 ?: は条件式の値により、2者のうちのどちらかを選択します。 $a=($x<10)?10:20; 変数$aの値が$x<10という条件において、真であれば:左側の10を選び、偽であれば:右側の20を選ぶ。 $a=10; 変数$aに10を代入する。 $a==5?print "5です。\n":print "5ではありません。\n"; 条件式でaが5であれば:左側の print "5です。\n"を表示します。 条件式でaが5でなければ:右側の print "5ではありません。\n"を表示します。 $a==10?print "10です。\n":print "10ではありません。\n"; 条件式でaが10であれば:左側の "10です。\n" を表示します。 条件式でaが10でなければ:右側の "10ではありません。\n" を表示します。 範囲演算子 範囲演算子 .. は、左側の値から右側の値まで、1つずつ増やした値の集まりです。 値には、半角英数字の数値または文字列が指定できる。 print 3..5; 3 4 5 と表示する。 一応、訳を下に書いたのですが合っていますでしょうか。

  • 実数型浮動小数点の変数でのゼロ

    実数型の変数x,xxについて、以下のようなコードを書きました。 x=0.0 xx=-x write(*,*) x,xx これをコンパイルして実行すると、0.000, -0.000となります。xxにマイナスが付きます。この意味はx=0.0とおいても変数x,xxは実際は0となっておらず微妙に値を持っているからだと思います(無視できるほど小さいとは思いますが)。 これが気になる場合、本当のゼロにすることは可能なのでしょうか。NULLとするとか何かの方法で、ということですが。いちおうFortranなのですが、他の言語では何らかの方法があるかと思いますが。 ひょっとして不可能ということになるでしょうか。

  • C言語による演算時の誤差について

    C言語初心者でどなたかご教授お願いします。 小数点の演算を行うわけではないのですが、 演算を行う数値が大きすぎるために、double型を使用して以下の計算を行うつもりです。 計算結果はlong型(小数点以下切り捨て)です。 double a,b,c; long x; x = (long)((a*b/c/100) 例)  x=(long)((189000*105000/100000)/100) 上記のような計算を行った際でも誤差が発生する可能性があるのでしょうか?

  • C++ の new演算子について

    C++ の new演算子について質問です。 new演算子を用いてクラスのインスタンスを作ったときに、 クラスのメンバー関数内で使用される自動変数はメモリの何処に割り付けられますか? 以下の回答の内のいずれかと想定しています。 ・ヒープ領域 ・スタック領域 たとえば、以下のように、クラスTestClassが定義されていたとします。 class TestClass { int x; // int型(4byteとする) char y; // char型(1byte) long z; // long型(4byte) void play(short); } void main(void){ TestClass* pt = new a(); play(10); } void TestClass:: play(short n){ char a; long b; static c; for(int a = 0; a < 10; a++ ){ b = n * a; cout << b; } } main関数内で、インスタンスを作成した時点で ・TestClassのデータメンバx,y,z ⇒ ヒープ領域に確保(4+1+4 = 9byte。もしかしたらアライメント     の関係で もう少し大きく領域を確保するかも) ・play関数で使われる変数n,a,bの領域は何処に確保されるのでしょうか? 変数cは静的変数用領域に保存される? new演算子で作ったインスタンスはdelete演算子を使わないと消えないと勉強しました。(OSが消さない限り) つまり、上記ではmain関数を抜けても、変数x,y,z,n,a,bの実体は残ると考えてよいのでしょうか? そう考えると、n,a,bの実体はスタックではなく、ヒープ領域に確保する気がします、、 どうか、ご教授ください。

  • 浮動小数点表現

    浮動小数点表現の問題で以下の実行結果を元にfloat型変数 f に与えた実数の実際に格納されているビット列を表示するプログラムを作りたいのですが、 #include<stdio.h> main() { float a; scanf("%f",&a); printf("%f",a); } この程度までしか作れません。ポインタを使ってアドレスを表示することは分るのですが…。 どなたか教えていただけると助かります。 実行結果 Size of Float : 4 byte Size of Int : 4 byte f=0.500000000000000 00111111000000000000000000000000

  • 量子力学における演算子と変数

    量子力学におえける演算子とは何かよくわからなくて困っています プログラミングをやっているのでプログラミングの話をしますが、プログラミングにおいて演算子とは、感覚的に言えばある変数を引数にして処理をするための関数的なもの + - x / の四則演算子が基本 sin() exp()などが演算子と言われても理解できるのですが 量子力学では、位置を表すxやyなども演算子として扱うと言われ プログラミング的にはそれらは関数のオペランドであり変数なので混乱しています 誰か上手く説明できる方がいらっしゃったら教えて下さい Ψを使った演算子の交換についての項で詰まっています

  • プログラミングの分野

    プログラミングについて無知です。すみませんが、下に書いてある内容は、「OSS 」とか「Java言語」とか「Fortran言語」とか「UNIX」に関連する分野でしょうか ?関連するかしないか教えてください。 1 駅名の表示        ファイル名、注釈、文字列定数、エスケープ文字        システムのインストール 2 駅名の読み方       ヘッダーファイル、インクルードファイル、#include 3 駅名表示の切換え       変数、文字変数、ライブラリ関数、ラベル、goto文 4 曜日の算出       ネスト、書式、式、代入文、宣言、整定数、整変数、       代入演算子、(不)等値演算子、関係演算子、論理演算子、真/偽 5 借用権の値段       実数、実定数、実変数、実数名、評価 6 生年月日の入力       ブロック、複文、&演算子 7 利息計算       繰返し、ループ、ラベル(名札)、演算子の優先順序、       増分、初期設定 8 財産形成       演算子の優先順位、プログラムのトレース 9 結婚できるのはいつ       オーバーフロー、sizeof演算子、最適化、シフト演算子、       ループの種類 10 うるう年       関数の定義、関数の型、関数の値、パラメタ、仮引数、       実引数、返却値 11 法王の名前       ブロック構造、スコープルール、無限回ループ、       再帰的呼出し(recursive call)、コンマ演算子 12 おいらの予想       配列、#define、マクロ定義、long

  • 昇降演算子のブラケットの問題

    昇降演算子のブラケットの問題 以下の問題を解いたら、 ψ=D*exp(-cx^2/2)(c、Dは定数)となり、 下記画像の(5)式を使いませんでした。 どうやったら(5)式を使うのでしょうか。 どなたか教えていただけるとうれしいです。 -- 質量m、角振動数ωの1次元調和振動子の ハミルトニアンは(1)式で与えられる。 ここでp(^は省略します)は運動量演算子、 xは位置演算子であり、交換関係[x,p]=xp-px=ih/2πを満たす。 また、(2)、(3)で定義される2つの演算子を考える。 演算子N=a†aの固有値をnとし、 その規格化された固有状態を|n>とする。 すなわちN|n>=n|n>,<n|n>=1である。 次の2つの関係式が成り立つ。 a†|n>=√(n+1)|n+1>, a|n>=√n|n-1> 上記で定義された固有状態|n>の規格化された波動関数を ψ_n(x)=<x|n>とする。 ここで、|x>は演算子x(^は省略します)の固有状態である。 基底状態|0>の満たす条件a|0>=0を用いて、 ψ_0(x)=<x|0>を求めよ。なお、(4)、(5)の関係式を用いてもよい。

専門家に質問してみよう