none
MathクラスのSinメソッドとCosメソッドについて RRS feed

  • 質問

  • こんにちは、YORIGONです。

    とある、ゲームで指定された座標から次の座標へ移動するプログラムを書いていたのですが

    ちょっと気になることがありまして、投稿しました。

     

    移動量がもしVo(Single)だと考え移動角度がtheta(Single)として考えた場合以下の数式がでます。

     

    '移動量を算術

    x = Vo * Math.Cos(theta * (Math.PI / 180))

    y = Vo * Math.Sin(theta * (Math.PI / 180))

    '算術結果を表示

    DirectDrawText("x = " & CStr(x) & vbCrLf & "y = " & CStr(y) & vbCrLf & CStr(theta))

     

    っとこんな感じでとりあえずVoは1.0Fで固定で考えればVoは無視しているようなものなので

    とりあえずtheta変数だけ1.0Fずつ増やしていって90までカウントされたら理論上

    x = 0 で y = 1

    になるはずですよね

    ところが、Sinは正常に動作してるのかも知れませんが

     

    x = 6.12303176911189E-17

    y = 1

    90

     

    と表示されかなりびっくりしました。

    どこからこんな数値が出てくるのかさえもわかりません

    いろいろ上記の変数をDouble型にしてメソッドに合わせるようにもしてみましたが結果は同じ

    もしかしてCPUが吹っ飛んだのではと思い電卓を起動し90のcosをやってみたら正常に0と返ってきます。

     

    おかしいと思いMSDNを参照してみると

    Sinの戻り値の説明では

    a のサイン。aNaNNegativeInfinityPositiveInfinity のいずれかに等しい場合、このメソッドは NaN を返します。 』

    Cosの戻り値の説明では

    d のコサイン。』

    とこれは、まぁシンプルな戻り値だなぁと・・・・しかし戻り値の説明が若干違う気がするんです。

    やっぱりCosメソッドとSinメソッドは扱いは何か違うのでしょうか?

    数学的にはそうたいした変りようはないと思うのですが・・・・・・・

     

    さらに両メソッドのパラメータ説明は同じで『ラジアンで計測した角度。』となっています。

    これもシンプルな説明ですが・・・・・・・

     

    それと、一応自分のPCの環境を言うと

    開発環境 : VisualStudio.NET アカデミック 2005

    OS     : WindowsVista HomeBasic

    CPU    : AMD Sempron Mobile 3400+

     

    どうかこうなる原因と対処法を教えていただけないでしょうか?

    本当にお願いしますm(_ _)m

    2007年8月25日 11:08

回答

  • いろいろ調べた結果

     

    以下のソースコード流用すれば大丈夫みたいな感じです。

     

    Math.round( Math.cos( Math.PI/2 )*1000000000000000 ) / 1000000000000000

     

    なんか、すごい感じです。

    2007年8月25日 11:31

すべての返信

  • すみません、二重投稿です。

    自分も投稿後Yahooとかでいろいろ調べていたら

    Math.Cosにはバグが含まれていたようなので対処法をいろいろ調べてみます。

     

    本当にすみませんでした、対処法が見つかり次第ここに追記します。

     

    本当に何も調べる前に投稿することを失礼しましたm(_ _)m

    2007年8月25日 11:19
  • いろいろ調べた結果

     

    以下のソースコード流用すれば大丈夫みたいな感じです。

     

    Math.round( Math.cos( Math.PI/2 )*1000000000000000 ) / 1000000000000000

     

    なんか、すごい感じです。

    2007年8月25日 11:31
  •  DETTEIU さんからの引用

    どうかこうなる原因と対処法を教えていただけないでしょうか?

    本当にお願いしますm(_ _)m

     

    原因は計算精度で,対処法はケースバイケースです.

    浮動小数点数に表現誤差・計算誤差はつきものなので,別にバグというわけではありません.

     

    手元の環境では Math.PI が返す値は 3.14159265358979 で Math.PI/2 とすると 3.14159265358979  です.これらの値は,もちろん,数学上のπとは完全には一致しません.

    具体的には,Math.PI は真のπより 0.000000000000001 % ぐらい小さい値になっています.

     

    なお,文字列化に関しても注意が必要で,デフォルトのラウンドトリップ指定子と,明示的な桁指定で表示結果が変わります.

    Code Snippet

     

    Console.WriteLine("{0:f16}", Math.PI);
    Console.WriteLine("{0:R}", Math.PI);

     

    3.1415926535897900

    3.1415926535897931

     

     

    2007年8月25日 13:08
  • 外池ともうします。

     

    Nyaruruさんもご説明になっていますが、ご指摘になった計算結果は、まったく問題のないもので、浮動小数点演算の誤差に関する仕様の範囲に十分収まっています。-17乗のオーダーの数字なので、限りに無くゼロに近い数字と考えてよろしいかと。ゲームのような応用面であれば、まったく問題にならない誤差です。内部的な演算としては、Math.Round関数を使う必要はまったく無いと思います。

     

    もし、表示させる上で気持ちが悪い、ということであれば、表示させる際だけ、ToStringメソッドで適当にフォーマットを指定して、有効数字を数ケタ程度にしておけば、綺麗に"0.0000"と表示されるハズです。

     

    ごくごく一般論ですが、浮動小数点の演算は非常に細かい動きをすることがありますので気をつけてください。

     

    「1.0Fずつ増やしていって90までカウントされたら」という表現もありましたが、これも、実は注意が必要です。

     

    1.0Fは、小数点以下の部分がないので、整数の場合とまったく同じように厳密に加算されて、確かに、90.0Fにキッチリ一致した結果が得られます。ところが、0.1F刻みだったらどうか? 0.01F刻みだったらどうか? 数学的には900回、あるいは、9000回の加算で90.0Fにきっちり一致するハズですが、浮動小数点数の場合は、そうなるとは限らないです。

    2007年8月25日 15:04
  • う~ん、皆様がたのご返答ご意見ありがとうございました。

    是非参考にさせていただきます。

     

    では、またよろしくお願いします。

    2007年8月26日 3:17