質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

2回答

3718閲覧

最適化オプションによって結果が変わるプログラムを治したい

valgrind_val

総合スコア13

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

2グッド

2クリップ

投稿2018/09/03 08:21

編集2018/09/03 10:26

前提・実現したいこと

C言語を使って数値計算を行っており、プログラムのバグを見つけられず困っています。
バグの内容は
●同じプログラムを実行しても、計算機ごとに結果が違う
●-O0オプションをつける,つけないで結果が違う
(訂正:iccの17.0.1を用いた場合に,この現象が生じます.
gccの8.2.0を用いた場合はこの現象は起きませんでした.)
●特定の箇所にprintfを入れると違っていたはずの結果が一致する.
という感じです.

valgrind等のデバッグツールでメモリリークの検査は行いましたが,
検出されませんでした

###質問
バグを見つける為に元々のプログラムを削り,小さいプログラムを作成しました.
間違っている点がありましたら教えてください.
(このプログラムでも上記のバグが検出されました)

プログラムの内容は、ザックリ言うと配列skから配列grを計算する物です.
(元のプログラムを削ったものなので,
何でこんなものを計算したいのか意味不明でしょうが,
許してください)

該当のソースコード

#include<stdio.h> #include<math.h> #include<stdlib.h> #define dx (0.5) #define Nmeas 2 #define Lx 128 int main(void){ int e=0,i=0,j=0,k=0; double r=0,l=0; double ***sk,**gr; sk=(double***)calloc(Lx,sizeof(double**)); for(i=0;i<Lx;i++){ sk[i]=(double**)calloc(Lx,sizeof(double*)); } for(i=0;i<Lx;i++){ for(j=0;j<Lx;j++){ sk[i][j]=(double*)calloc(Nmeas,sizeof(double)); } } if(sk==NULL){ printf("sk_dame\n"); exit(EXIT_FAILURE); } //skを格納 for(k=0;k<Nmeas;k++){ for(i=0;i<Lx;i++){ for(j=0;j<Lx;j++){ sk[i][j][k]=i*(j+1); } } } // gr=(double**)calloc(Lx,sizeof(double*)); for(i=0;i<Lx;i++){ gr[i]=(double*)calloc(Nmeas,sizeof(double)); } if(gr==NULL){ printf("gr_dame\n"); exit(EXIT_FAILURE); } for(e=0;e<Nmeas;e++){ for(k=0;k<Lx;k++){ r=dx*k; //-- for(i=0;i<Lx;i++){ for(j=0;j<Lx;j++){ if(i<=Lx/2-1){ if(j<=Lx/2-1){ l=sqrt(i*i*dx*dx+j*j*dx*dx); } else{ l=sqrt((Lx-i)*(Lx-i)*dx*dx+(Lx-j)*(Lx-j)*dx*dx); } } else{ if(j<=Lx/2-1){ l=sqrt(i*i*dx*dx+j*j*dx*dx); } else{ l=sqrt((Lx-i)*(Lx-i)*dx*dx+(Lx-j)*(Lx-j)*dx*dx); } } if(r<=l&&l<r+dx){ gr[k][e]=gr[k][e]+sk[i][j][e]; //ここにprintfすると結果が一致する } } } //----- } } for(k=0;k<Nmeas;k++){ for(i=0;i<Lx;i++){ r=dx*i; printf("%d %f %0.10f \n",k,i*dx,gr[i][k]); } } for(i=0;i<Lx;i++){ free(gr[i]); } free(gr); for(i=0;i<Lx;i++){ for(j=0;j<Lx;j++){ free(sk[i][j]); } } for(i=0;i<Lx;i++){ free(sk[i]); } free(sk); return 0; }

計算結果
文字数がオーバーしてしまうので,最後の方だけ載せます
(左がiccのオプションなし,右がオプションありです.gccではオプションに寄らず右の結果になりました)

1 30.000000 33477799.0000000000 | 1 30.000000 873054.0000000000 1 30.500000 34425182.0000000000 | 1 30.500000 854069.0000000000 1 31.000000 34212931.0000000000 | 1 31.000000 855826.0000000000 1 31.500000 35155964.0000000000 | 1 31.500000 865433.0000000000 1 32.000000 34910260.0000000000 | 1 32.000000 826290.0000000000 1 32.500000 35211832.0000000000 | 1 32.500000 824195.0000000000 1 33.000000 35450545.0000000000 | 1 33.000000 707294.0000000000 1 33.500000 35677466.0000000000 | 1 33.500000 728292.0000000000 1 34.000000 35906161.0000000000 | 1 34.000000 738308.0000000000 1 34.500000 36078876.0000000000 | 1 34.500000 647620.0000000000 1 35.000000 36247586.0000000000 | 1 35.000000 679681.0000000000 1 35.500000 36388883.0000000000 | 1 35.500000 622576.0000000000 1 36.000000 36545293.0000000000 | 1 36.000000 682210.0000000000 1 36.500000 36663200.0000000000 | 1 36.500000 628288.0000000000 1 37.000000 36784525.0000000000 | 1 37.000000 615445.0000000000 1 37.500000 36880832.0000000000 | 1 37.500000 612941.0000000000 1 38.000000 36975446.0000000000 | 1 38.000000 588217.0000000000 1 38.500000 37068071.0000000000 | 1 38.500000 618596.0000000000 1 39.000000 37142861.0000000000 | 1 39.000000 553947.0000000000 1 39.500000 37210905.0000000000 | 1 39.500000 578542.0000000000 1 40.000000 37277654.0000000000 | 1 40.000000 576153.0000000000 1 40.500000 37343204.0000000000 | 1 40.500000 531098.0000000000 1 41.000000 37424960.0000000000 | 1 41.000000 587050.0000000000 1 41.500000 37471419.0000000000 | 1 41.500000 505516.0000000000 1 42.000000 37528185.0000000000 | 1 42.000000 555003.0000000000 1 42.500000 37577489.0000000000 | 1 42.500000 526944.0000000000 1 43.000000 37619491.0000000000 | 1 43.000000 517908.0000000000 1 43.500000 37650663.0000000000 | 1 43.500000 521416.0000000000 1 44.000000 37679802.0000000000 | 1 44.000000 470481.0000000000 1 44.500000 37722918.0000000000 | 1 44.500000 540990.0000000000 1 45.000000 37734753.0000000000 | 1 45.000000 478670.0000000000 1 45.500000 9553650.0000000000 | 1 45.500000 483596.0000000000 1 46.000000 9713700.0000000000 | 1 46.000000 485267.0000000000 1 46.500000 9852311.0000000000 | 1 46.500000 477315.0000000000 1 47.000000 9997536.0000000000 | 1 47.000000 472017.0000000000 1 47.500000 10125488.0000000000 | 1 47.500000 457102.0000000000 1 48.000000 10254449.0000000000 | 1 48.000000 443522.0000000000 1 48.500000 10397168.0000000000 | 1 48.500000 471996.0000000000 1 49.000000 10511010.0000000000 | 1 49.000000 446795.0000000000 1 49.500000 10630727.0000000000 | 1 49.500000 445005.0000000000 1 50.000000 10737178.0000000000 | 1 50.000000 420705.0000000000 1 50.500000 11019140.0000000000 | 1 50.500000 441648.0000000000 1 51.000000 10980396.0000000000 | 1 51.000000 432992.0000000000 1 51.500000 11061778.0000000000 | 1 51.500000 398384.0000000000 1 52.000000 11181792.0000000000 | 1 52.000000 417967.0000000000 1 52.500000 11415638.0000000000 | 1 52.500000 409181.0000000000 1 53.000000 11372659.0000000000 | 1 53.000000 414329.0000000000 1 53.500000 11453674.0000000000 | 1 53.500000 401703.0000000000 1 54.000000 11518515.0000000000 | 1 54.000000 371639.0000000000 1 54.500000 11770976.0000000000 | 1 54.500000 387916.0000000000 1 55.000000 11709783.0000000000 | 1 55.000000 398142.0000000000 1 55.500000 11768876.0000000000 | 1 55.500000 372310.0000000000 1 56.000000 11847745.0000000000 | 1 56.000000 375489.0000000000 1 56.500000 12053578.0000000000 | 1 56.500000 359779.0000000000 1 57.000000 11994805.0000000000 | 1 57.000000 382065.0000000000 1 57.500000 12044179.0000000000 | 1 57.500000 362027.0000000000 1 58.000000 12081815.0000000000 | 1 58.000000 335297.0000000000 1 58.500000 12150407.0000000000 | 1 58.500000 351626.0000000000 1 59.000000 12344066.0000000000 | 1 59.000000 339022.0000000000 1 59.500000 12405260.0000000000 | 1 59.500000 351818.0000000000 1 60.000000 12288796.0000000000 | 1 60.000000 325346.0000000000 1 60.500000 12323267.0000000000 | 1 60.500000 314558.0000000000 1 61.000000 12373140.0000000000 | 1 61.000000 326031.0000000000 1 61.500000 12568242.0000000000 | 1 61.500000 343289.0000000000 1 62.000000 12566745.0000000000 | 1 62.000000 301647.0000000000 1 62.500000 12610059.0000000000 | 1 62.500000 315659.0000000000 1 63.000000 12465081.0000000000 | 1 63.000000 285269.0000000000 1 63.500000 12520344.0000000000 | 1 63.500000 318055.0000000000 ``` ### 補足情報(FW/ツールのバージョンなど) 用いたコンパイラは gcc 8.2.0とiccの17.0.1です
Zuishin, atata0319👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2018/09/03 10:09

VC では再現しませんでした。それぞれの結果はどうなりますか?
valgrind_val

2018/09/03 10:11 編集

ごめんなさい.VCとは何でしょうか? それぞれの結果というのは,コンパイル結果を載せろと言うことでしょうか?
Zuishin

2018/09/03 10:12

Visual Studio です。コンパイル結果ではなく、実行結果がコンパイルオプションによって違うということではないのですか?
valgrind_val

2018/09/03 10:15

そうです.困っているのはコンパイルオプションによって計算結果の数値が違ってしまうと言うことです.
Zuishin

2018/09/03 10:18

質問を編集して結果を掲載してください。
valgrind_val

2018/09/03 10:27

結果を全て載せると文字数オーバーになるので,最後の方だけ載せました
Zuishin

2018/09/03 10:36

VC も右です。icc で最適化を有効にした時だけ結果が違うのでコンパイラのバグっぽいですね。
valgrind_val

2018/09/03 10:39

なるほど、ありがとうございます。回答にもたついてしまい、すみませんでした
valgrind_val

2018/09/03 10:47

この辺りもかなり怪しそうですね。ありがとうございます
guest

回答2

0

ベストアンサー

iccの最適化ミスでしょう。

printfによって値が変化する事から
多重ポインタのせいで変数grの値がループ中に更新されないと認識され
並列化されてしまっている可能性が推測できます。

とりあえず、試したいのが
gr[k][e]=gr[k][e]+sk[i][j][e];gr[k][e]+=sk[i][j][e];に書き換える事です。
これによりgr[k][e]の値が変化してくからi,jのforは並列化できないとコンパイラが認識してくれる……かもしれません。

ダメだったらdouble ***sk,**gr;volatile double ***sk,**gr;に書き換える事になるかと思います。
(ただし、最適化の効果は著しく落ちる事でしょう)


追記

c

1 //-- 2 double tmp = 0.0; // 追加 3 for(i=0;i<Lx;i++){ 4 for(j=0;j<Lx;j++){ 5 if(i<=Lx/2-1){ 6 if(j<=Lx/2-1){ 7 l=sqrt(i*i*dx*dx+j*j*dx*dx); 8 } 9 else{ 10 l=sqrt((Lx-i)*(Lx-i)*dx*dx+(Lx-j)*(Lx-j)*dx*dx); 11 } 12 } 13 else{ 14 if(j<=Lx/2-1){ 15 l=sqrt(i*i*dx*dx+j*j*dx*dx); 16 } 17 else{ 18 l=sqrt((Lx-i)*(Lx-i)*dx*dx+(Lx-j)*(Lx-j)*dx*dx); 19 } 20 } 21 22 if(r<=l&&l<r+dx){ 23 // gr[k][e]=gr[k][e]+sk[i][j][e]; 24 // ここにprintfすると結果が一致する 25 tmp += sk[i][j][e]; // 変更 26 } 27 } 28 } 29 gr[k][e] = tmp; // 追加 30 //-----

推測通り(ポインタ変数のせいで変数の更新が誤認されているのが問題)ならば
一時変数を用いて変数の更新を正しく認識させられるかもしれません。

投稿2018/09/03 22:45

編集2018/09/04 07:31
asm

総合スコア15147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

valgrind_val

2018/09/04 02:27 編集

回答ありがとうございます. gr[k][e]+=sk[i][j][e]; に置き換えてもうまく行かなかったので, volatile double ***sk,**gr; に書き換えようとしています. しかしvolatileの使用方法がわかりません. sk=(volatile double***)calloc(Lx,sizeof(volatile double**)); (他の箇所も同様) のように書き換えたのですが, free(gr[i]); , free(sk[i][j]); の部分で 「型 "volatile double *" の引数が型 "void *" の引数と互換性がありません」 とエラーが出ます.よろしければ解決法を教えていただけませんか
asm

2018/09/04 07:19

free((void*)gr[i]); 等で対処できませんか? 考えてたら別解を思いついたので追記します
valgrind_val

2018/09/04 07:44

ありがとうございます おかげさまで,最適化の有無に寄らず結果が一致しました. 私はポインタを勉強したてで,初心者な質問にも答えて頂き大変助かりました.
asm

2018/09/04 09:30

今回のケースは最適化コンパイラについて「何やってるのか」を理解してないと難しいですね。 iccは特に最適化で売ってるコンパイラなので 最適化対策とでも言うべき小細工をしたソース 適正なコマンドラインオプション どちらかが欠けると計算結果に誤りが生じます。
guest

0

まあこういうときはSTLのstd::vectorとかstd::arrayのメンバ関数atを使うように書き換えるのがデバッグの常套手段なので書き換えます。

すると

#include <stdio.h> #include <math.h> #include <stdlib.h> #include <iostream> #include <array> constexpr double dx = 0.5; constexpr std::size_t Nmeas = 2; constexpr std::size_t Lx = 128; int main(){ std::array<std::array<std::array<double, Nmeas>, Lx>, Lx> sk {}; std::array<std::array<double, Lx>, Nmeas> gr {}; //skを格納 try{ for(std::size_t k=0;k<Nmeas;k++){ for(std::size_t i=0;i<Lx;i++){ for(std::size_t j=0;j<Lx;j++){ sk.at(i).at(j).at(k)=i*(j+1); } } } } catch(const std::exception& er){ std::cout << '1' << er.what() << std::endl; return 1; } for(std::size_t e=0;e<Nmeas;e++){ for(std::size_t k=0;k<Lx;k++){ const double r=dx*k; //-- for(std::size_t i=0;i<Lx;i++){ for(std::size_t j=0;j<Lx;j++){ double l=0; if(i<=Lx/2-1){ if(j<=Lx/2-1){ l=sqrt(i*i*dx*dx+j*j*dx*dx); } else{ l=sqrt((Lx-i)*(Lx-i)*dx*dx+(Lx-j)*(Lx-j)*dx*dx); } } else{ if(j<=Lx/2-1){ l=sqrt(i*i*dx*dx+j*j*dx*dx); } else{ l=sqrt((Lx-i)*(Lx-i)*dx*dx+(Lx-j)*(Lx-j)*dx*dx); } } try{ if(r<=l&&l<r+dx){ gr.at(k).at(e)=gr.at(k).at(e)+sk.at(i).at(j).at(e); //ここにprintfすると結果が一致する } } catch(const std::exception& er){ using std::endl; std::cerr << '2' << er.what() << endl << "gr: size()=" << gr.size() << ", k=" << k << endl << "gr[k]: size()=" << gr[0].size() << ", e=" << e << endl << "sk: size()=" << sk.size() << ", i=" << i << endl << "sk[i]: size()=" << sk[0].size() << ", j=" << j << endl << "sk[i][j]: size()=" << sk[0][0].size() << ", e=" << e << endl; return 2; } } } } } for(unsigned int k=0;k<Nmeas;k++){ for(unsigned int i=0;i<Lx;i++){ // const double r=dx*i; try{ printf("%u %f %0.10f \n",k,i*dx,gr.at(i).at(k)); } catch(const std::exception& er){ std::cerr << '3' << er.what() << std::endl; return 3; } } } }

のようになります。

実行すると

https://wandbox.org/permlink/UK7HYjzAgRZEIDfr

2array::at gr: size()=2, k=2 gr[k]: size()=128, e=0 sk: size()=128, i=0 sk[i]: size()=128, j=2 sk[i][j]: size()=2, e=0

となり、kの値がおかしいことがわかります。

まあつまり、多分書き換え後で

gr.at(k).at(e)

としているところが、ke逆なんじゃないかって気がします。

追記: 私が書き換えミスってただけで元のプログラムのこの部分はasmさん指摘の通りあってますね・・・うーむ。

投稿2018/09/03 10:41

編集2018/09/03 20:49
yumetodo

総合スコア5850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

valgrind_val

2018/09/03 10:46

今急用で外出していまして、ゆっくり見れないですが、解決しそうです! ありがとうございます
valgrind_val

2018/09/03 12:40

すみません。C++を使ったことないので教えて頂きたいです. 上に載せて下さったエラーの意味は 「配列grは128×2の大きさで定義したはずなのに, 途中で2×128の配列のように処理されている」と言うことでしょうか? それで「gr[k][e]=gr[k][e]+sk....」の部分を 「gr[e][k]=gr[e][k]+sk....」と直すべきだと言うことですか?
yumetodo

2018/09/03 13:24

ちなみに例外メッセージはarray::atとしか言ってくれないみたいです、この処理系では。
valgrind_val

2018/09/03 13:26

わかりました。ありがとうございます
asm

2018/09/03 15:10

std::array<std::array<double, Lx>, Nmeas> gr {}; じゃなくて std::array<std::array<double, Nmeas>, Lx> gr {}; じゃないですか?
yumetodo

2018/09/03 20:46

あー本当だ。するとこの回答は正しくないな・・・
valgrind_val

2018/09/04 07:45

わかりました.回答も訂正して頂きありがとうございます
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問