続 signbit
とりあえず思いっきり処理系依存な方法で実装しました。
http://hamigaki.sourceforge.jp/hamigaki/math/sign_bit.hpp
<math.h>でsignbit()マクロが提供されているか、i386の環境でだけ動作します。
調べた限り、i386環境ではfloat=4バイト、double=4/8バイト、long double=8/10/12/16バイトでした。gccはオプションでdoubleを4バイトに変更可能です。またlong doubleは、VisualC++とCodeWarriorで8バイト(64ビット)、Borlandで10バイト(80ビット)、gccで既定は12バイト(80ビット+後ろに16ビットのパディング)、オプションで16バイト(80ビット+後ろに48ビットのパディング)に変更可能といった具合です。パディングの部分の値は不定のようです。
符号ビットはパディングを除いた(最後のバイトの)最上位ビットなので、浮動小数点型のサイズに応じて確認するビット位置を変更するだけで実装できます。
floatの場合は、こんな感じです。
int sign_bit(float t) { return (*((unsigned*)&t) & 0x80000000) != 0; }
邪悪なキャストが入っていますが、移植性は捨てているので気にしてはいけません。
これに対し、VC8は次のようなコードを吐いてくれます。(gcc -fomit-frame-pointer -O2でも同じ)
mov eax,dword ptr [esp+4] shr eax,1Fh ret
フレーム・ポインタの退避が省略されています。インライン・アセンブラを用いて、
int sign_bit(float t) { __asm { mov eax,dword ptr [ebp+8] shr eax,1Fh } }
と書いても、フレーム・ポインタの退避は挿入されてしまうので、おいしくありません。
一応、
__declspec(naked) int sign_bit(float t) { __asm { mov eax,dword ptr [esp+4] shr eax,1Fh ret } }
とすれば全く同じ結果は得られますが、コンパイラ毎に記述を変えるのが面倒くさいだけです。
というわけで、邪悪なキャストが一番速くて(i386環境内で)移植性が高かったのでした。