見出し画像

MathWorks か IEEE かどっち NaN だい!?~ atan2 の定義域を巡って

Twitter で 「Simulink の atan2(0,0) が NaN にならない」という投稿を見ました。

atan2 とは、MATLAB のみならず一般的に、arctan($${=tan^{-1}}$$)、つまり tan の逆関数であり、さらに引数を2つ持つことが特徴です。


まずは tan の定義から

$$
x=r \cos\theta\\
y=r\sin\theta\\
r=\sqrt{x^2+y^2}
$$

とすると $${tan = y/x}$$ です。
$${\theta=0}$$ のときは $${y=0}$$ なので $${tan(0)=0}$$ です。 
$${x = 0}$$ では $${tan}$$ は定義されません
たぶん高校で習いました。


そしてその逆関数である arctan には、計算上ややこしい問題があります。

tan の グラフはこうです。

tan

この逆関数ということは y 軸から x 軸の値にするということですが、一つの y の値に対して、複数の x の値が出てきます。つまり、象限(x, y の符号)を考慮に入れる必要があります。また、(y/x) を入力とすると、x = 0 で 0 割が発生してしまいます。

このような使いにくい関数であるため、atan2 が考案されました。

atan2 は(基本的には)

$$
atan2(y, x) = arctan\big(\frac{y}{x}\big)
$$

です。

引数を x と y の二つにすれば、少なくとも入力時に 0 割は起こりませんし、(y/x) と (-y/-x)、(-y/x) と (y/-x) を区別できます。


ここで残る問題は、定義されていない x=0 の時をどうするかです。

定義されてないので「正解」の数値はありません。


かといって NaN を返されたのでは、例外処理が必要になりこれまた面倒です。


浮動小数点演算に関する国際的な取り決めである IEEE754 - IEEE Standard for Floating-Point Arithmetic では、

$${atan2( 0, 0)=0}$$

$${atan2(-0 ,0)=-0}$$

$${atan2( 0, -0)=\pi}$$

$${atan2(-0 ,-0)=-\pi}$$

と決められています。


最近の C でもこの実装です。

つまり、C でも NaN は返さないようになっています。


ただ MATLAB では、全て 0 としています。


確認してみましょう。

MATLAB/Simulink でアルゴリズム開発 ~ C/C++ の検証を MATLAB で ~ C Caller / C Function でやったように、C Caller ブロックを使って Simulink 上で比較してみます。

各 C Caller の設定は全て同じで

ブロックパラメーター
インクルードヘッダー
ソースファイル
#ifndef _ATAN2C_C_H_
#define _ATAN2C_C_H_

extern double atan2C(double y, double x);

#endif

atan2C_c.h

#include <math.h>
double atan2C(double y, double x)
{
    return(atan2(y,x));
}

atan2C_c.c

Simulink モデル実行結果


このように、C では IEEE754 準拠、MATLAB では全て 0 という実装になっています。


極まれに MathWorks さん、世間標準に刃向かうパンクなところがあるので、何かおかしな事が起こったときに MATLAB の結果を鵜呑みにするのではなく、自分の知識と照らし合わせて確認する必要はありますね。(u_u)



「C でも NaN は返さない」辺り、「Simulink モデル実行結果」でしかちゃんと示していなかったので、文章でも追加しました。

2022/12/18









この記事が気に入ったらサポートをしてみませんか?