トップページ > 過去ログ > 記事閲覧
acos: DOMAIN errorを表示させない方法
名前:スー 日時: 2008/12/11 22:40

acosを使っていると時たまacos: DOMAIN errorというエラーが出てきます。 このエラーがでてくるのはacosが-1〜1以外の範囲外になれば出るというのはわかるのですが、-1〜1の範囲にでてくることがあります。 私の式が間違えていると言われればそれまでなのですが、このエラーが出てこないようにする方法はありませんか?(DXライブラリの中身をつついてエラーのダイアログを出ないようにできませんか?)

Page: 1 |

Re: acos: DOMAIN errorを表示させない方法 ( No.1 )
名前: 日時:2008/12/12 10:42

どの様にその確認を行っていますか? ソースコードと環境を明確にして頂けますか? acos関数は定義域エラーが発生すると、 errnoにドメインエラーかアウトレンジを 設定しますがC9X以上の環境では数学ライブラリが、 広域エラー領域に値を設定することは 必須とされていません。 #C89以前ではerrnoのチェックは必須ですが。。 >エラーのダイアログを出ないようにできませんか どんなエラーダイアログが出るのでしょう? もう少し詳しくお願いします。
Re: acos: DOMAIN errorを表示させない方法 ( No.2 )
名前:IW 日時:2008/12/13 03:36

>-1〜1の範囲にでてくることがあります  ほんとですか?  環境と具体的なソースを提示してみて下さい。  その環境を持っている人がいい解決法を提示してくれるかもしれません。 >このエラーが出てこないようにする方法はありませんか?(  現段階で、一番確実なのは acosと同等の機能を自作することです。
Re: acos: DOMAIN errorを表示させない方法 ( No.3 )
名前:Sura 日時:2008/12/13 11:15

もし、直接acosを使うのでしたら無視してください。 acosの代わりにatanを使うというのはどうでしょうか? IW様が提案される自作の具体例ですが acos(x) = y x = cos(y) (1/x)^2 = 1/cos(y)^2 = 1 + tan(y)^2 tan(y) = sqrt((1/x)^2 - 1) y = atan(sqrt((1/x)^2 - 1)) (1) (1)式を計算する前にif文などで sqrt()の引数が0以上のときのみとする。 何れにしても-1<=x<=1には違いないのですが
Re: acos: DOMAIN errorを表示させない方法 ( No.4 )
名前:スー 日時:2008/12/13 14:33

通さん、IWさん、Suraレスありがとうございます 環境は BCC Developer Ver1.2.21 DXライブラリ Ver2.25です。 通さんが言われているC89、C9Xなどは確認の仕方がわかりません。borlandのbcc55の事でしたら最近インストールしました。 またVisual C++ 2005で同じものを実行してみたところエラーダイアログはでてきませんでした。 BCC Developerでもこのエラーを出さない方法があれば教えてください。 acosを使わない方法もやってみるつもりです。 以下ソースです。 //四角と丸の当たり判定 丸の中心が四角に入っていたら自機の下に「HIT」と表示する #include<math.h> #include"DxLib.h" #define PI 3.1415936 #define RAD 57.29578 float timer; int cr; int i ; float plx=320,ply=240,plsize=30; float angle = 0;//四角の角度 float si_x=150,si_y=300;//四角の座標 int ss_x,ss_y;//四角のサイズ float px[5],py[5];//四角の四点の座標 初期状態の[0]左上 [1]右上 [2]右下 [3]左下 //A=初期状態の左上 B=右上 C=左下 D=左下 //Z=自機 double AZx,AZy,AZz,BAx,BAy,BZx,//AZxはA点からZ点までの横の距離 BZy,BZz,BAz,CZx,CZy,CZz,//AZyは縦の距離 CDx,CDy,CDz,DZx,DZy,DZz,//AZzは三平方の定理で長さを求める ADx,ADy,ADz,CBx,CBy,CBz;// double baz,dcz,adz,cbz;//それぞれの角度 int number_2(){ AZx = fabs( plx - px[0]); AZy = fabs( ply - py[0]); AZz = sqrt( AZx*AZx + AZy*AZy); BAx = fabs( px[0] - px[1]); BAy = fabs( py[0] - py[1]); BAz = sqrt( BAx*BAx + BAy*BAy); BZx = fabs( plx - px[1]); BZy = fabs( ply - py[1]); BZz = sqrt( BZx*BZx + BZy*BZy); CZx = fabs( plx - px[2]); CZy = fabs( ply - py[2]); CZz = sqrt( CZx*CZx + CZy*CZy); CDx = fabs( px[2] - px[3]); CDy = fabs( py[2] - py[3]); CDz = sqrt( CDx*CDx + CDy*CDy); DZx = fabs( plx - px[3]); DZy = fabs( ply - py[3]); DZz = sqrt( DZx*DZx + DZy*DZy); ADx = fabs( px[0] - px[3]); ADy = fabs( py[0] - py[3]); ADz = sqrt( ADx*ADx + ADy*ADy); CBx = fabs( px[1] - px[2]); CBy = fabs( py[1] - py[2]); CBz = sqrt( CBx*CBx + CBy*CBy); baz = acos( (AZz*AZz + BAz*BAz - BZz*BZz)/(2*AZz*BAz) ) * RAD;//三角形abcがありそれぞれの長さがわかれば dcz = acos( (CZz*CZz + CDz*CDz - DZz*DZz)/(2*CZz*CDz) ) * RAD;//角度が出すことが出来る公式 adz = acos( (ADz*ADz + DZz*DZz - AZz*AZz)/(2*ADz*DZz) ) * RAD;//(b^2+c^2-a^2)/(2bc) cbz = acos( (CBz*CBz + BZz*BZz - CZz*CZz)/(2*CBz*BZz) ) * RAD;//これによりaに対する角度?がでる if(baz <= 90 && dcz <= 90 && adz <= 90 && cbz <= 90)//円の中心が四角の中に入っている return 1; return 0; } int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ){ SetGraphMode( 640 , 480 , 16 ); ChangeWindowMode( TRUE ); if( DxLib_Init() == -1 ) return -1; SetDrawScreen( DX_SCREEN_BACK ); char Buf[ 256 ] ; int sikakuimg = LoadGraph( "sikaku.bmp" ) ; //この画像は横140縦209の長方形です GetGraphSize( sikakuimg , &ss_x , &ss_y ) ; while(1){ ClearDrawScreen(); timer = GetNowCount(); GetHitKeyStateAll( Buf ) ; cr = GetColor( 255 , 255 , 255 ) ; if(angle>=360)angle-=360; DrawRotaGraph( si_x , si_y , 1.0f , angle/RAD , sikakuimg , TRUE ) ; DrawCircle( plx,ply,plsize , cr ) ; cr = GetColor( 255 , 255 , 255 ) ; DrawFormatString( 320, 0 , cr, "angle = %lf°", angle ); float x,y,z; x = ss_x/2; y = ss_y/2; z = sqrt(x*x+y*y); float O = atan(y / x)*RAD; DrawFormatString( 320, 20 , cr, "O = %lf°", O ); cr = GetColor( 255 , 255 , 255 ) ; px[0] = (si_x ) - z*cos(O/RAD + angle/RAD) ; py[0] = (si_y ) - z*sin(O/RAD + angle/RAD) ; px[1] = (si_x ) + z*cos(O/RAD - angle/RAD) ; py[1] = (si_y ) - z*sin(O/RAD - angle/RAD) ; px[2] = (si_x ) + z*cos(O/RAD + angle/RAD) ; py[2] = (si_y ) + z*sin(O/RAD + angle/RAD) ; px[3] = (si_x ) - z*cos(O/RAD - angle/RAD) ; py[3] = (si_y ) + z*sin(O/RAD - angle/RAD) ; cr = GetColor( 255 , 0 , 0 ) ; DrawLine(plx,ply, px[0],py[0],cr); DrawLine(px[1],py[1],px[0],py[0],cr); DrawLine(plx,ply,px[2],py[2],cr); DrawLine(px[3],py[3],px[2],py[2],cr); cr = GetColor( 0 , 255 , 0 ) ; DrawLine(plx,ply, px[1],py[1],cr); DrawLine(px[2],py[2],px[1],py[1],cr); DrawLine(plx,ply,px[3],py[3],cr); DrawLine(px[3],py[3],px[0],py[0],cr); DrawFormatString( 320, 50 , cr, "baz = %lf", baz ); DrawFormatString( 320, 70 , cr, "dcz = %lf", dcz ); DrawFormatString( 320, 90 , cr, "adz = %lf", adz ); DrawFormatString( 320, 110 , cr, "cbz = %lf", cbz ); DrawFormatString( 320, 210 , cr, "dx = %3.lf", px[3] ); DrawFormatString( 320, 230 , cr, "dy = %3.lf", py[3] ); DrawFormatString( 320, 250 , cr, "cx = %3.lf", px[2] ); DrawFormatString( 320, 270 , cr, "cy = %3.lf", py[2] ); DrawFormatString( 320, 290 , cr, "zx = %3.lf", plx ); DrawFormatString( 320, 310 , cr, "zy = %3.lf", ply ); DrawFormatString( 200, 350 , cr, "acos((CZz*CZz + CDz*CDz - DZz*DZz)/(2*CZz*CDz))" ); DrawFormatString( 200, 370 , cr, "acos((%7.lf + %7.lf - %7.lf)/(%9.lf))",CZz*CZz,CDz*CDz,DZz*DZz,2*CZz*CDz ); DrawFormatString( 200, 390 , cr, "acos((%27.lf)/(%9.lf))",CZz*CZz+CDz*CDz-DZz*DZz,2*CZz*CDz ); DrawFormatString( 200, 410 , cr, "acos(%lf)",(CZz*CZz+CDz*CDz-DZz*DZz)/(2*CZz*CDz) ); //私がエラーを確認したのはangle = 180°で自機がCD線上にいるときにエラーがでます。 //なのでこの式はdczの中身を書いています。 //dcz = acos( (CZz*CZz + CDz*CDz - DZz*DZz)/(2*CZz*CDz) ) * RAD; if(number_2() == 1){ DrawString( plx , ply + plsize , "HIT中!!" , cr ); } if( Buf[ KEY_INPUT_Z ] == 1 ) angle-=1; if( Buf[ KEY_INPUT_X ] == 1 ) angle+=1; if( Buf[ KEY_INPUT_RIGHT ] == 1 ) plx += 4; if( Buf[ KEY_INPUT_LEFT ] == 1 ) plx -= 4; if( Buf[ KEY_INPUT_UP ] == 1 ) ply -= 4; if( Buf[ KEY_INPUT_DOWN ] == 1 ) ply += 4; ScreenFlip(); while( GetNowCount() - timer < 17 ){} if( ProcessMessage() == -1 ) break; if( CheckHitKey( KEY_INPUT_ESCAPE ) == 1 ) break; } DxLib_End(); return 0; }
Re: acos: DOMAIN errorを表示させない方法 ( No.5 )
名前:IW 日時:2008/12/13 15:40

>float O = atan(y / x)*RAD;  この式がおかしいです。  最初のこの式が実行されたとき、x=0になっています。  その為、y / xの結果が非数になり atanに渡しているので、戻り値の Oの値も非数になります。  続いて、Oに依存する pxや pyも非数になり、そのまま AZxyz、BAxyz、CZxyzなども 非数になった為、acosに失敗しているものと思われます。  というか、普通にデバッグすれば、かなり初期の段階から値がおかしくなっていることに 気付きませんか?  さて、ダイアログを出さなくする手ですが、borland C++ 5.5.Xの場合ソースのどこかに int _matherr(struct _exception *__e) { return 1; }  を入れて下さい。  数学関数でエラーが起きたとき、この関数が実行されるようになります。  0以外を戻すことで、既定の動作(この場合はエラーダイアログを出す)を行わなくなります。    しかし、この方法は今回の場合は根本的に数学関数への入力値が異常なことが 原因であり、そのチェック機構を無効にするとバグの原因特定が遅れることになるので、 デバッグ時は_matherr()は空関数にはせず、何らかのエラー表示をすることをお薦めします。
Re: acos: DOMAIN errorを表示させない方法 ( No.6 )
名前:Sura 日時:2008/12/13 16:44

>IW様 GetGraphSize( sikakuimg , &ss_x , &ss_y ) が成功していれば >float O = atan(y / x)*RAD; は、問題ないと思います。 しかしながらそれ以外の事柄は全く同意するものであり、 GetGraphSize()が、成功しているかどうかの確認 及び、失敗したときのルーチンを考えるべきと思います。 >スー様 あと、関数number_2()内の acos()を使う手前で、 以下のコードを追加してみてください if((AZz*AZz + BAz*BAz - BZz*BZz)/(2*AZz*BAz) > 1) MessageBox(NULL,TEXT("∠BAZについて、acos(t)の t>1 です。"),NULL,MB_OK); if((AZz*AZz + BAz*BAz - BZz*BZz)/(2*AZz*BAz) < -1) MessageBox(NULL,TEXT("∠BAZについて、acos(t)の t<-1 です。"),NULL,MB_OK); if((CZz*CZz + CDz*CDz - DZz*DZz)/(2*CZz*CDz) > 1) MessageBox(NULL,TEXT("∠DCZについて、acos(t)の t>1 です。"),NULL,MB_OK); if((CZz*CZz + CDz*CDz - DZz*DZz)/(2*CZz*CDz) < -1) MessageBox(NULL,TEXT("∠DCZについて、acos(t)の t<-1 です。"),NULL,MB_OK); if((ADz*ADz + DZz*DZz - AZz*AZz)/(2*ADz*DZz) > 1) MessageBox(NULL,TEXT("∠ADZについて、acos(t)の t>1 です。"),NULL,MB_OK); if((ADz*ADz + DZz*DZz - AZz*AZz)/(2*ADz*DZz) < -1) MessageBox(NULL,TEXT("∠ADZについて、acos(t)の t<-1 です。"),NULL,MB_OK); if((CBz*CBz + BZz*BZz - CZz*CZz)/(2*CBz*BZz) > 1) MessageBox(NULL,TEXT("∠CBZについて、acos(t)の t>1 です。"),NULL,MB_OK); if((CBz*CBz + BZz*BZz - CZz*CZz)/(2*CBz*BZz) < -1) MessageBox(NULL,TEXT("∠CBZについて、acos(t)の t<-1 です。"),NULL,MB_OK); 私の環境でやったところ ∠BAZ, ∠DCZ, ∠ADZ, ∠CBZについて、 -1を下回っていることが判りました。 ちなみに、 浮動小数演算は、演算が複雑になるほど 誤差が大きくなるので、 #define PI や RAD はできるだけ正確にするのと 余弦定理を使うよりも たとえば、内積による計算のほうが良いと思います。
Re: acos: DOMAIN errorを表示させない方法 ( No.7 )
名前:IW 日時:2008/12/13 16:45

>GetGraphSize( sikakuimg , &ss_x , &ss_y )が成功していれば  あー、そっか。確かにそうですね。  大変失礼しました。
Re: acos: DOMAIN errorを表示させない方法 ( No.8 )
名前:スー 日時:2008/12/13 17:03

>IWさん ダイアログを消す方法を教えてくださりありがとうございます。 しかし、エラー表示の重要性がわかりましたので、消さないようにしたいと思います。 >Suraさん これからはGetGraphSizeに限らず、他の関数にも失敗したときの処理を入れていきたいと思います。 誤差がここまで影響してくるとは思いもしませんでした。内積での計算の勉強してみたいと思います。 皆様、おかげでいろいろ理解することができました。 ありがとうございます。

Page: 1 |