【C++】double の計算結果がおかしくなる

このQ&Aのポイント
  • double型の計算結果が状況によっておかしくなる問題について質問します。
  • 具体的なコード例を示しながら、問題の原因やバグについての情報を教えてください。
  • Windows環境上での現象やその他状況についても詳しく教えていただけると助かります。
回答を見る
  • ベストアンサー

【C++】double の計算結果がおかしくなる

ouble の計算結果が状況によっておかしくなる場合があるのですが、 理由がなかなか分からないので心当たりがあれば教えてもらえませんか。 問題のコードを簡略化したのが以下です。 ------------------------------------ void calc(double *result) {   const double mPI2 = 1.5707963267948966;   double b_arc;   b_arc の計算 … (1)   // printf("test"); … (2)   *result = mPI2 - b_arc; … (3) } ------------------------------------ おかしくなるのは(3)の *result の値です。 (3)での b_arc、mPI2 の値を出力してみると b_arc: [1.57079632679489660000000000000000000000] mPI2: [1.57079632679489660000000000000000000000] となり、mPI2 == b_arc の結果も true になります。 さらに、バイナリで見ても二つは全く同じ値でした。 なので *result は 0 になるはずなのですが、これが 0 にならない場合があります。 色々な状態で試したところ結果は以下のようになりました。 1. Windows7 : 0 2. WindowsXP : -0.000000000000000001951563910473908 3. WindowsXPのデバッグビルド : 0 4. WindowsXPで(2)(2)のコメントアウトを外し printf をいれる : 0 WindowsXPのリリースビルドだけ 0 にならず、 -0.000000000000000001951563910473908 になります。 4は理由が分からないのですが、(2)の位置になんらかの処理をいれると結果が 0 になります。 ちなみにVC++ではなくQTというFrameworkを使って作っています。 よろしくお願いします。

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

  • ベストアンサー
  • qwertfk
  • ベストアンサー率67% (55/81)
回答No.3

おそらく、下記リンクにあるような浮動小数用レジスタの扱いが原因だと思います。 http://0xcc.net/blog/archives/000164.html doubleはメモリ上では64bitですが、実際にプロセッサ内で計算する場合には、モードや動作環境にもよりますが64bit以上のサイズのレジスタ(80bit)が使われることが多いようです。 したがって、レジスタにはいっている値をメモリに戻す時に精度の低下が発生します。 b_arc: [1.57079632679489660000000000000000000000] とありますが、プロセッサ内で浮動小数演算を連続して実行し続けている場合、レジスタ内ではもう少し値が入っており、 b_arc: [1.5707963267948966********] という感じになっているはずです。そこから mPI2: [1.57079632679489660000000000000000000000] を引くため、正しい計算結果としては 0.0000000000000000******** となり、この差分が > 2. WindowsXP : -0.000000000000000001951563910473908 では無いかと思います。 > 4は理由が分からないのですが、(2)の位置になんらかの処理をいれると結果が 0 になります。 というのは、途中で別の命令が入ってくるため浮動小数演算がいったん中断され、80bitレジスタの値を64bitのメモリにダウンコンバートしながら移動する、すなわちb_arcの値を [1.5707963267948966*****] → [1.57079632679489660000000000000000000000] と変換する処理が途中に入ってしまい、結果としてmPi2==b_arcとなっている、というのが理由だと思います。

zigzagfire
質問者

お礼

どうもありがとうございます。 リンクも参考になりました。 No1の方も同じことをおっしゃっていますし、 その可能性が高いということで対応していこうと思います。

その他の回答 (2)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

余談ですが「VC++ではなくQTというFrameworkを使って作っています」といわれてもねぇ. コンパイラを確定させてほしい....

zigzagfire
質問者

補足

コンパイラはGCC(MinGW)です。

回答No.1

憶測ですが……。 b_arc の計算で、三角関数の計算があるとして、 b_arc に結果が代入される直前の、拡張精度(double より、さらに桁数の多い計算、計算の中間で使われることはある)の変数を、最適化の結果として、*result への代入に使っているのではないかなという気はします。 だから、result への代入の前に別の処理が入ると、この最適化ができずに、b_arc の値をまじめに使って計算しているとか。 アセンブラに落ちた結果を確認すると良いですが。 もしくは、リリースビルドの最適化をOFFにして結果がどう変わるか確認してみるとか。

zigzagfire
質問者

お礼

回答ありがとうございます。 確かにそれだと何か処理をいれると値が変わることに説明がつきますね。 ネットで調べてみたのですが、アセンブラは値が見れませんでした。 全然アセンブラの知識がないので調べるのはちょっと時間がかかりそうです。

関連するQ&A

  • javaのdoubleでの計算による誤差

    宜しくお願いします。 割り算でなくても、足し算の小数点計算で誤差がでます。 public class keisan { public static void main(String[] args) { double a1 = 12.3; double b1 = 45.6; System.out.println(a1 + b1); } } 結果が 57.900000000000006 となります。 きっちり57.9とならないのはなぜでしょう? 他の数字でも数が小数点以下でブレます。 原因に何が考えられるでしょうか? OSはWindowsXP pro ver.2002 SP3 32bit jdk 1.6.0_17 eclipseは Eclipse Platform Version: 3.5.2.R35x_v20100210-0800-9hEiFzmFst-TiEn9hNYgDWg1XN8ulH_JvCNGB Build id: M20100211-1343 を使っています。

    • ベストアンサー
    • Java
  • C&C++、計算結果を表示する

    超初心者です。 本をみつつ勉強しているところなんですが、演習問題などやっていると、 コンパイルしてビルドして実行すると計算結果は目に見える形で出ずに終了・・するんですね。 それはprintf~がないから・・らしいということはココで聞いたりして微妙に判ったんですが、その記載の仕方を知りたいんです。 もしも『printf~~、と書けばどんな計算結果も表示される』というのがあるのなら、それを、そうじゃないのなら、その記述の仕方の決まりを教えてください。 計算の過程が見えないとできてるのかできてないのかわからなくて・・・。 よろしくお願いします。 一応、例を載せておきます。 main() { int a,b,i; a=b=i=0; while (i<100){ i++; if(i%2==0)a=a=i; else b =b+i; } }

  • C90とC99の計算結果の違い?

    C言語の質問です。 gcc version 4.3.2 (Ubuntu 4.3.2-1ubuntu12) 以下のプログラムをgccでコンパイル・実行すると(1)に入り,"a"が出力されます。unsigned intの計算なのでラップアラウンドが生じtest=4294716272となるのは私の期待どおりです。 ただ、gcc -std=c99でコンパイル・実行すると(2)に入り,"b"が出力されました。c99でコンパイル・実行すると計算結果がなぜ異なっているのかが分かりません。 long test = 0; unsigned int x = 184; unsigned int y = 251208; test = (x-y); if(test == 4294716272){ printf("a");// (1) }else if(test == -251024){ printf("b"); // (2) }

  • 実数型の変数に値を入力した計算結果がおかしくなる理由

    こんばんは。 以下のプログラムで値がおかしくなる理由、改善方法を教えて頂けませんでしょうか。 double a,b,c; scanf("%f",&a); scanf("%f",&b); c = a + b; printf("%f",c); これを実行すると、正しい値が出力されません。 int型で宣言し、整数表示の%dに変更すると問題なく値が出力されます。 また、予めdouble型で宣言した変数a,bに実数の初期値(8.5等)を格納しておくと正しい値が出力されます。 どなたかご回答よろしくお願い致します。

  • C言語で計算が合いません(初心者)

    C言語で計算が合いません(初心者) 簡単な計算のプログミングをしているのですが 計算が合わなくて困っています。 整数型変数aと実数型変数bを定義 整数型変数cとdを定義して、c=0.5*a*b、d=a/2*b 実数型変数eとfを定義して、e=0.5*a*b、f=a/2*b という内容のプログラムを作ったのですが a=7,b=2.4を代入してみた結果 ---------------------------- Input a>>7 Input b>>2.4 8 7 8.400000 7.200000 ---------------------------- となりました どこが間違っているのでしょう? 答えは8.4でcが8なのは有効数字のためだと思いますが dとeで答えが全く違う理由が分かりません。 下に私が書いたものを貼っています。 ---------------------------- #include<stdio.h> int main(void){ int a,c,d; double b,e,f,ans; printf("Input a>>"); scanf("%d",&a); printf("Input b>>"); scanf("%lf",&b); c=0.5*a*b; printf("%d\n",c); d=a/2*b; printf("%d\n",d); e=0.5*a*b; printf("%lf\n",e); f=a/2*b; printf("%lf\n",f); return 0; } ---------------------------- よろしくお願いします。

  • 計算結果を出力するコード

    ある参考書に 「次の計算結果を出力するコードを記入せよ」 との問題がありました 問   0-4   3.14*2   5 / 3   30 / 7のあまりの数   (7+32)/ 5 これのコードを作成したのですが答えがなくて自己流で作成したものが正しいのか判断がつきません もっとスマートにコードを組み立てられるのであれば教えてください 自分で作ったコードです #include <stdio.h> int main(void) { int a, b, sum; double aa, bb, cc, ssum; a = 0; b = 4; sum = a+b; printf("a+bは%d\n", sum); aa = 3.14; bb = 2; ssum = aa*bb; printf("aa*bbは%f\n", ssum); a = 5; b = 3; ssum = (double)a/(double)b; printf("a/bは%f\n", ssum); a = 30; b = 7; sum = a%b; printf("a%%bは%d\n", sum); aa = 7; bb = 32; cc = 5; ssum = (aa+bb)/cc; printf("(aa+bb)/ccは%f\n", ssum); return 0; } 採点お願いします。。。

  • 複利の利率を求めるプログラムをC言語で作りたいんですが、、、

    計算を始める値、最終的な結果、計算回数(期間)、の3つから利率を求めたいんですが、、 単利の利率を求める公式から、利率を0.001ずつ減らすループを作れば、誤差0.001以内の値が出るのではないかと思ったんですが、、全く動いてくれなせん、何故でしょうか?? #include<stdio.h> double main() { double x; double s; double k; int counter1; double a; double b; double r; int counter2; printf("初期値=?\n"); scanf("%lf",&x); printf("結果=?\n"); scanf("%lf",&s); printf("期間=?\n"); scanf("%d",&counter1); k=((s-x)/(counter1*x));//*単利での利率の公式*// printf("単利での利率(%%)=%f\n",k*100); while(counter1==counter2){ b=x;//*bはxを上書きさせない為*// k=k-0.001; while(b<=s){ //*複利の計算*// a=b/100; r=a*k; b=b+r; counter1++; printf("複利での利率(誤差0.001)=%f\n",k);} }return 0;} whileの部分ををforで、 for(b=x;counter1==counter2;k-0.001){ for(counter1=1;b<=s;counter1++){ としても、、コンパイルはできるのですが、、ループ自体をしてくれません、、何故なのでしょうか??C言語以外でもいいですが、、複利の利率は公式とかはあるんでしょうか??

  • 複利の利率を求めるプログラムをC言語で作りたいんですが、、、

    計算を始める値、最終的な結果、計算回数(期間)、の3つから利率を求めたいんですが、、 単利の利率を求める公式から、利率を0.001ずつ減らすループを作れば、誤差0.001以内の値が出るのではないかと思ったんですが、、全く動いてくれなせん、何故でしょうか?? #include<stdio.h> double main() { double x; double s; double k; int counter1; double a; double b; double r; int counter2; printf("初期値=?\n"); scanf("%lf",&x); printf("結果=?\n"); scanf("%lf",&s); printf("期間=?\n"); scanf("%d",&counter1); k=((s-x)/(counter1*x));//*単利での利率の公式*// printf("単利での利率(%%)=%f\n",k*100); while(counter1==counter2){ b=x;//*bはxを上書きさせない為*// k=k-0.001; while(b<=s){ //*複利の計算*// a=b/100; r=a*k; b=b+r; counter1++; printf("複利での利率(誤差0.001)=%f\n",k);} }return 0;} whileの部分ををforで、 for(b=x;counter1==counter2;k-0.001){ for(counter1=1;b<=s;counter1++){ としても、、コンパイルはできるのですが、、ループ自体をしてくれません、、何故なのでしょうか??C言語以外でもいいですが、、複利の利率は公式とかはあるんでしょうか??

  • C言語の関数に関する質問ですが

    C言語の初心者です。よろしくお願いいたします。 授業でこのような演習が出ました。 演習:実数x を入力したときの最大値を求めるプログラムを作れ. 実数x を入力すると,x; -x; x2; xの絶対値の平方根 の中で一番大きい値を答える プログラムを作れ(ファイル名はmax.c とする). 表示は以下のようにする. Input x: -0.5 【Enter】 Answer is 0.707107. #include<stdio.h> #include<math.h> double max(double a, double b){ if( a > b) return a; else return b; } int main(void) { double x,y; printf(\"Input x: \"); scanf(\"%lf\",&x); y = max (x,-x); y = max (y,x*x); y = max (y,sqrt(fabs(x))); printf(\"Answer is %f.\\n\",y); } このように書けばうまく実行できますが、関数の中に関数を使えないでしょうか。うまく言えないですが、たとえば、以下のように書いてみましたが、うまく実行できません。どう直したらいいでしょうか、お忙しい中教えていただけたらうれしいです。 #include <stdio.h> #include <math.h> int max(double a,double b) { if (a<b) return b; else return a;} int main(void) { double x,result; printf(\"Input x:\"); scanf(\"%lf\",&x); result=max(max(x,-x),max(pow(x,2),sqrt(fabs(x)))); printf(\"%.2f\",result); return 0; } よろしくお願いいたします!!

  • double型の減算がうまくいかない

    Java初心者です。 今、Javaの勉強中で、double型の計算を行っています。 double型同士の減算を行った結果、思っていた結果と違う値が帰ってきたので、原因、もしくは解決方法をお教え頂ければと思います。 ソースは以下 public static void main(String[] args) { //値A double longitudeA = -73.98289; //値B double longitudeB = -73.98315; BigDecimal aDecimal = new BigDecimal(String.valueOf(longitudeA)); BigDecimal bDecimal = new BigDecimal(String.valueOf(longitudeB)); //減算 BigDecimal decimal = aDecimal.subtract(bDecimal); //小数点以下第5位で四捨五入し、出力 System.out.print(decimal.setScale(5, RoundingMode.HALF_UP).doubleValue()); } 出力される値として、 0.00026 を想定していたのですが、実際に出力されたのは 2.6E-4 でした。 BigDecimalのオブジェクトをそのまま出力すると想定通りの結果が出力されます。 一回Stringに型変換を行った後、Double#parseDoubleを行ったりしてみたのですが、 どうも思った通りの出力がされません。 解決策をお教え頂ければと思います。 よろしくお願いします。

専門家に質問してみよう