2013/06/26

VC++のpow関数でエラー検出

pow(x,y)やると_clearfp()で_SW_INEXACTが出るよねぇ
あれどういう仕組み?
(Power関数のエラーハンドリングについて調べたり試したりして書いてます。
 実際に使う場合はちゃんとテストしてください。)
#include <math.h>
#include <cfloat>
double x = 2.0;
double y = 3.0;
unsigned int rv = _clearfp();
double ans = pow(x, y);
rv = _statusfp();

if ((rv & _SW_INVALID) > 0) {
   //領域エラー xが負で、yが整数でない有限値
} else if ((rv & _SW_ZERODIVIDE) > 0) {
   //極エラー xがゼロで、y が負
} else if ((rv & _SW_UNDERFLOW) > 0 ||
           (rv & _SW_OVERFLOW ) > 0 || !_finite(ans)) {
   //double範囲外エラー
} else {
   //正常に計算終了(丸め誤差発生「rv == _SW_INEXACT」の場合はエラーにしません)
}

このときrvは_SW_INEXACT(0x0001)が設定されます。これはdouble値の計算によって丸め誤差が発生してるのでansの値は正確ではない(かもしれない)よ、という意味です。2の3乗なんて整数の8に決まってますが倍精度浮動小数点数の制限としてビシィっとした「8」ではないよ、という意味です。これはどうしようもないのでrvが_SW_INEXACT単体のエラーであれば無視してansを計算結果として処理し、対応はアプリの使用者に任せるのが良いです。

エラー値はビットで、_SW_INEXACTと_SW_OVERFLOWが同時に設定されたりすることがあるようです。そのため「==」ではなく「&」演算して値が0でないなら、というような判定をしてます(0でない整数値はtrue扱いなので比較は無用なんですが、うーむ)。

この_clearfp()と_statusfp()は簡単に確認した限り、スレッドセーフだと思います。

オーバーフローに関して!

32ビット版のWindowsで動かすとオーバーフローのビットが設定されません。頑張って調べてますが今のところ理由は不明です。オーバーフロー以外は設定されるのでオーバーフローは _finite(x) でもチェックをかけて32ビット版Windowsでも問題が起きないようにしました。それに伴い、オーバーフローのチェックを最後にしました。

まとめ!

この手の計算に関して、判明していることを書き残しておきます。
この手の浮動小数の計算では計算精度とエラー検出をコントロールできます。
_controlfp(newVal, mask)を使います。float.hの定義と睨めっこしながら、maskには変更する対象、newValには変更する値を与えます。ビットなのでOR連結で一気に設定も出来ますが個々にやった方が分かり易いです。
unsigned int rv = 0;
_clearfp(); // ステータス値をクリア
unsigned int def = _controlfp(0, 0); // 現在の値を確保
_controlfp(_MCW_EM_MCW_EM); // 全部のエラーを検出する
_controlfp(_RC_NEAR_MCW_RC); // 丸め誤差はNEARとする(他DOWN、UP等)
_controlfp(_PC_64_MCW_PC ); // 制度は64ビットとする(他53、24ビット)

double ans = pow(x, y);
rv = _clearfp(); // 直前のステータス値を取得し、同時にクリアする

_controlfp(def, _MCW_EM | _MCW_RC | _MCW_PC); // 元の値に戻す
ビット演算なのでOR連結で一気に指定も出来ますが個々にやった方が良いでしょう。例えば、検出するエラーを指定する場合は以下のようにやると良いでしょう(でもハンドルしてないエラーになるので止まるかもしれません)。
_controlfp(_EM_INEXACT | _EM_UNDERFLOW | _EM_OVERFLOW, 
             _MCW_EM); // 検出するエラーを指定する
計算精度についてサンプルコードで確認したところ、デフォルトではWin32ビルドでは53ビットが設定されています。x64ビルドでは_PC_64が設定されているように見えますがこれは_PC_64が0x00なのでそう見えるだけで、x64では_MCW_PCに0以外を与えるのはいけないらしいです。つまりx64での計算精度は53ビット固定です。x64のDebugビルドでは_MCW_PCに0以外を設定しようとするとAssertionエラーが起こり、Releaseビルドでも無効なパラメーターの例外が出るそうです
MSDN(計算精度の違いで結果が違うことを確認するコードが載ってます。「x64 アーキテクチャでは、浮動小数点の精度の変更はサポートされていません」と書いてあります。)

サンプルコードでは計算後に_clearfp()でステータスを取得しています。直前のステータス値を取得しつつ、クリアできるのでこの方がスマートですが、「あぇ?ステータス取得する前にclearやっちゃってん?おぇ??」みたいになるかもしれないので_statusfp()で取得→_clearfp()でクリア、の方が読み易いんじゃないかなぁとも思います。

(「_controlfp(_EM_OVERFLOW, _EM_OVERFLOW );」とやってもやっぱり32ビットのWindowsではオーバーフローは検出できないようです。「コンパイラが調子悪い」状態ですTT)

さらに「_EM_DENORMAL」というエラーはなんとなく無視していました。これはつまり「非常に小さい値ですよ」という理解で良いんでしょうか?
「計算できなかった」というエラーではなさそう(?)なので、今のとこアンダーフローじゃないならこのエラーは無視してて良いか…
なんか_controlfpでこのエラーを検出しないで良い、と設定しても検出されます。なんででしょう。

2013/06/23

パラダイスキス

パラダイス・キス」見ました。だいぶ前ですがテレビでやってたので。
もろに女性向けの映画のようであまり面白くなかったかな。
北川景子がびんたされるシーンでほんとに顔が切れちゃう事故があったそう。そういえばヒステリックな母親にびんたされるシーンがあった気がする。その母親とも仲直りできたようで良かったですね。
出てくる人たちが才能があって超お金持ちの駆け出しのデザイナーとか、成績優秀で進学校に通っててモデルにスカウトされちゃう美人の女の子とかだから、なーんかね。
そういう設定はノルウェイの森に似てるのかもしれないけどあっちの方が雰囲気が良かった分だけ良かった。

インデペンデンス・デイ」見ました。これもテレビで。
何度見ても面白い。この映画の見所はやっぱり戦闘機のドッグファイトだね。混乱した状況で即席でチーム作るところとかエースコンバット6の最初のシーンを思い出した。

2013/06/18

NULLチェック

MSDNより。 http://msdn.microsoft.com/ja-jp/library/cc440197(v=vs.71).aspx
C++ 暗黒時代(1994 年ごろまで)は、大部分のコンパイラの operator new は NULL を返していました。
この動作は C の malloc に対する妥当な外挿でした。
幸いなことに、私たちが生きている時代はもっと進んでいて、コンパイラは強く、クラスは美しく、そして標準ライブラリの operator new は例外をスローします。
「1994 年ごろまで」
20年も経ってるのにまだnew後のNULLチェックが大量にある。というより今も増え続けてるだろ…

2013/06/17

コメントについて

既存のコードで、コメントが多くていかにも複雑なのを読むとき、ふと思いついてコメントを全部削除、詰めてインデント整えてみると意外と読めるようになった経験がある。

ソースコードのコメントは必要なところにだけあると役に立つ。
一行ずつコメントがあるのは論外だけど、ちょっと読めば分かるようなところにコメントが書かれているのもダメだよね。
実感として、単にコメントが多いってだけで読むのがうんざりするコードになるし、逆にコメントが無くて複雑で何をやってるのか分かんないコードも読むのがうんざりする。
「コメントを読めば読解に役に立つって部分にしかコメントを付けちゃいけない」んだと思う(この感覚が難しんだけど)。
ついでに言うとプログラムの仕様についてコードの中にコメントで書くのもダメだと思う。コードの読解には関係ないし、それは他のテキストか、メソッドの説明に書くべきだよね。

書いていいコメントは、フィールドの説明とかメソッドの説明(JavaDocになるやつ)と、すっごい変なコードで何をやったのか分かんないだろうなと思う部分だけ。例えば「このループは文字列の”¥”を取り除きたいだけです」とか書くと、その珍妙なループは読み飛ばしてokだ、と分かるようになるし(あるいはリファクタリングされる)、「配列x、y、zはフーリエ変換して部分標準偏差から平均熱量分布を…」とか書くとその部分は読み飛ばされるか、そのソースコードは閉じられて二度と開かれないかな。
何が言いたいかというと、変なコードを書かない限り、コメントは不要だということ。

もしもコードを書いてて、コメントを書こう、と思ったなら、今書いたコードか、これから書くコメントか、どちらかが変なんだよ。
コードが変だ(あるいは数学的で難しい)、と分かってる時だけ(そして様々な理由でそのコードを改善することが出来ないときだけ)コメントを書こう。

2013/06/07

Copy Constructorって

コピーコンストラクタなんて一度も書いたことないけど、それで困ったことも一度もないよ。
要らないんじゃない?

コピーコンストラクタについて書かれたページの説明で多いのは、関数にオブジェクトを値渡しすると、オブジェクトのメンバーにポインタがあった場合deleteが2回呼ばれて落ちるっていう例。
int _tmain(int argc, _TCHAR* argv[])
{
   CSample obj;
   function( obj );
   return 0;
}
 
void function(CSample obj) ←実行時というよりこの時点でイクナイ
{
   //...

そこでコピーコンストラクタを定義して解決って流れだけど、その前にJavaから入ったオブジェクト脳のプログラマは『関数にオブジェクトを値渡し』って時点でアウトと思うはず。そこは参照渡し(かポインタ渡し)でないと。


もう一つ、代入演算子を自分で定義することについて。
「=」を自分で定義するなんて馬鹿らしいと思って考えもしなかった。が、スタック領域に生成したオブジェクトを代入演算子で上書きするとやはりdeleteがダブって落ちるらしい。これはJava脳的にはうまいことやってくれんじゃないかと思って書いてしまいそうな気がした。
   CSample obj;
   CSample obj2;
   obj = obj2; //←これはやっちゃいそう
最近の流儀ではコピーコンストラクタと代入演算子をPrivateに空っぽで宣言してしまうとコピーも代入も出来なくなるから安全とか、そのように定義したNoncopyableというクラスを作って継承すると良いとかあるらしいが、つまり言語仕様の欠陥ってことだよね。

Javaに無くてC++にあるものは疑ってかかれ
(Effective C++には代入演算子もコピーコンストラクタもきっちり定義しろって書いてるけど無くても何とかなるってwたぶんw)

2013/06/04

C++ に instanceof が無い!

C++でJavaの"instanceof"を行う方法。
参考:cprogramming.com
template <typename Of, typename What>
inline bool instanceof(const What &w)
{
  return dynamic_cast<const Of*>(&w) != 0;
} 
と定義して
 instanceof<Foo>(bar)
で行けるようだ。
この書き方では bar はポインタでなく直接の値を渡して判定する。
↓のようにするとポインタ渡しで判定できる。
template <typename Of, typename What>
inline bool instanceof(const What w)
{
  return dynamic_cast<const Of*>(w) != 0;
} 

 instanceof<Foo>(barPtr)
になる。どっちが良いのか分からんがJavaと同じ感覚で使えるのは後者か?

このinstanceofはざっくり言ってキャスト出来るか出来ないかを返して欲しい機能。
具体的にはアニマルクラスとその派生のキャットクラス、ドッグクラスがある場合、アニマルとして渡されたポインターが、ほんとはキャットなのかドッグなのか判断したいときに使える。(OOP的には場合分けする処理じゃなくてアニマルのまま処理できるようにした方が良いんだろうが何もかもうまく作れる分けじゃないし)

これっていきなりdynamic_castしてみてNULLじゃなければ、って書いても同じことだな…

VC++にisinf、finiteが無い!

(Pow関数をきっちり使うならこっちの記事が良いです)
math.pow(x,n)の結果がINF(数値外だかfloating point exceptionだか)になってしまったのでエラーハンドリングしようと思った。
しかし間違いなく<math.h>をインクルードしてるにも関わらず isinfも finiteも定義されていないと言われる。
しかしどんなに調べてもC++にはmath.hにisinf、finiteがあるの一点張り。

VC++では<float.h>をインクルードしてアンダースコア付きの _finite という関数を使うと良いらしい。
#include <float.h>
~~
if(!_finite(ans)){
   return -1;
}
C++とVC++って何でこんなびみょ~~な違いがあるの?

2013/06/02

メガネは弱めが良いか

一日中パソコンを見てるのでパソコン用に近距離メガネを作っている。これは視力低下予防と眼精疲労予防のためで、視力低下予防はともかく眼精疲労予防には絶大な効果を実感している。
中途半端に0.8くらいの眼鏡を作ったこともあるけど会議でプロジェクターの画面見るときとか遠くの看板見るときとかストレスになるよね…。なので今持ってるメガネは左右とも1.0以上の完全矯正の眼鏡と1mくらいでぼやけ始めるような弱い眼鏡の2種類、これをかけ変えながら仕事をしている。
すると「もう老眼かよ」なんて言われることもあるが別に完全矯正メガネでも普通にPC見れるって。
でも完全矯正のメガネで8時間もの間、1mも離れてないディスプレイを凝視するなんてやっぱ不自然な状態だと思うんだ。

近視がなぜ起こるか、について
  • 目が成長することで網膜が後ろにずれてピントが合わなくなった
  • 近くを見続けてそれに適応した
の2つとある。
(でも近くを見続けてそれに適応できるのは主に成長期ではないかと思う)
(成長期に近くを見続けてしまったから網膜が後ろにずれたと思う(1と2は関係があるんじゃないか?))
(だいたい成長すると遠くが見えなくなるなんて生き物としておかしい。その時点で嘘くさい。野生動物でもそんなこと起こるのか?)(遠くしか見ないマサイ族?は目がすごく良いというのをTVで見たし)
(大人になってから近くを見続けても、ちょっとは適応するかもしれないが多くは単に「目が痛ぇ~」状態になるだけだと思う)。

成長による視力低下は遺伝なのでまぁ仕方ない。
近くを見続けてなるのは近くを見ないようにすればよい。しかしパソコンを遠ざけるわけにもいかない。そこで遠くを見るのと同じ状態、脱力した状態で近距離にピントが合うメガネを作ったというわけ。眼鏡を外すだけだとピントが近く過ぎるし片方は乱視が強いので弱めの眼鏡が必要だった。

簡単な話だと思うが、「なぜ目が悪くなるか、またその予防方法は」という話題に対して人それぞれに立場とか持論があり、それを否定すると異様に熱くなるのだ(レーシック業界とレーシック以外の眼科医業界とメガネ屋業界かな)。
この件について何が起きているのかよく分かんないが、近視や近距離用メガネに関して調べる際はよーく気を付けよう。
メガネ屋も眼科医も儲けのために自分の目を悪くしようとしてる、という陰謀論に取りつかれないように。(特に2chの眼鏡板とWikipediaの近視の項目は何かおかしいよw)
(近視の項目について追記:視力検査は暗い部屋で

そういえば自分が子供の頃、目が悪くなり始めたときは、仮性近視と言われて強制的に瞳孔を開く目薬を処方された。あれで近視が防げる人って本当に居るのか?

ちなみに裸眼視力2.0の人は老眼鏡かけてパソコン作業すると良いと思うんだけどそういう人は居ないのかな?
ブルーライトカットとかよりよっぽど効果あると思うけど。

PCの見過ぎで目が痛いと訴えてた同業の友人(裸眼の人)に「100円ショップで老眼鏡が買えるんだから4000円もするブルーライトカットメガネ買う前に試してみろ」と進言したがなんか納得してもらえずにJins PCを買ってたなぁ。
結構強い近視の人にPC用の近距離メガネを作ってみては?と進言してもなんか「いやぁそれは・・」って言われちゃうんだよなぁ。

レーシックについてはメガネ以上に荒れてるようだ。事故ると取り返し付かないし、お金も掛かってるんだろう…。
個人的には他に目に問題が無いなら十分にお金かけて良い設備で施術すればだいじょぶだと思うんだけど(プロスポーツ選手もやってるようだし)。まぁレーシック後に別の問題が起きた時(白内障とか)のこと考えると慎重になるかな。

完全に矯正しない弱めのメガネだと視力が悪くなるという説もある。ぼやけた状態が続くと目がもっと悪くなるそうだ。ということはメガネが無かった時代は加速度的に目が悪くなるってことになるんじゃないかな?ちゃんとしたメガネを作れるようになった現代は近視が少なくなりそうだが、傾向として近視の人は増えてるんじゃなかったかな?
ちょっとこれは疑わしい説だ。

近距離用メガネを作るのに多少抵抗があるのは、メガネ屋(あるいは眼科)で近距離に合わせたメガネが欲しいというのを説明するのが面倒そう、というのがあるかもしれない。
メガネ屋は当然完全矯正に慣れてるだろうし、近距離用といっても1m先で良いのか2mなのか、左右ちゃんと違和感なく出来るか、 確認も時間かかるし、「やっぱり右をも少し弱めで」なんて調整も必要になりそうだし。
それは確かに事実だけど、それでも作る価値はあると思うよ。

眼鏡関係の記事: 視力検査は暗い部屋で