IEEE 754浮動小数点数を表す32ビットが与えられた場合、(機械語命令またはコンパイラー操作を使用して変換するのではなく)表現で整数またはビット演算を使用して、数値を整数にどのように変換できますか?
次の機能がありますが、場合によっては失敗します。
入力:int x(IEEE 754形式で32ビットの単精度数を含む)
if(x == 0) return x;
unsigned int signBit = 0;
unsigned int absX = (unsigned int)x;
if (x < 0)
{
signBit = 0x80000000u;
absX = (unsigned int)-x;
}
unsigned int exponent = 158;
while ((absX & 0x80000000) == 0)
{
exponent--;
absX <<= 1;
}
unsigned int mantissa = absX >> 8;
unsigned int result = signBit | (exponent << 23) | (mantissa & 0x7fffff);
printf("\nfor x: %x, result: %x",x,result);
return result;
Cには、このタイプのデータのビューを処理する「ユニオン」があります。
typedef union {
int i;
float f;
} u;
u u1;
u1.f = 45.6789;
/* now u1.i refers to the int version of the float */
printf("%d",u1.i);
_&x
_はxのアドレスを与えるため、_float*
_タイプを持っています。
_(int*)&x
_は、そのポインターをint
へのポインターに、つまり_int*
_モノにキャストします。
*(int*)&x
は、そのポインタをint
値に逆参照します。 int
とfloat
のサイズが異なるマシンでは、信じられないことを行います。
そしてエンディアンの問題があるかもしれません。
このソリューションは 高速逆平方根 アルゴリズムで使用されました。
(誰かがこの答え、特に境界ケースと負の値の丸めを再確認する必要があります。また、最も近い値に丸めるためにそれを書きました。Cの変換を再現するには、これをゼロに丸めるように変更する必要があります。)
基本的に、プロセスは次のとおりです。
32ビットを1つの符号ビット(s)、8つの指数ビット(e)、23の仮数ビット(f)に分けます。これらを2の補数の整数として扱います。
eが255の場合、浮動小数点オブジェクトは無限大(fがゼロの場合)またはNaN(それ以外の場合)のいずれかです。この場合、変換は実行できず、エラーが報告されます。
それ以外の場合、eがゼロでない場合は、2を追加します24fへ。 (eがゼロでない場合、仮数部の暗黙の前に1ビットがあります。2を追加24fでそのビットを明示的にします。)
eから127を減算します。 (これは、バイアス/エンコードされた形式から実際の指数に指数を変換します。一般的な変換を任意の値に行っている場合、eがゼロの場合に特別なケースを処理する必要があります。代わりに126を減算します。ただし、整数の結果に変換するだけなので、これらの小さな入力数値に対して整数の結果がゼロである限り、このケースは無視できます。)
sが0(符号は正)で、eが31以上の場合、値は符号付き32ビット整数(2です)をオーバーフローします。31 以上)。変換は実行できず、エラーが報告されます。
sが1(符号が負)で、eが31より大きい場合、値は符号付き32ビット整数からオーバーフローします(-以下) 232)。 sが1の場合、eは32、そしてfは2より大きい24 (元の仮数ビットが設定されていた場合)、値は符号付き32ビット整数からオーバーフローします(-2未満です)31;元のfがゼロの場合、正確に-2になります。31、オーバーフローしない)。これらのいずれの場合でも、変換は実行できず、エラーが報告されます。
これで、オーバーフローしない値のs、e、およびfが得られたので、最終的な値を準備できます。
sが1の場合、fを-fに設定します。
指数値は、1(両端を含む)から2(両端を含まない)までの仮数に対するものですが、仮数は2のビットで始まります24。ですから、それに合わせて調整する必要があります。 eが24の場合、有効桁数は正しいので完了です。結果としてfを返します。 eが24より大きいか24未満の場合、仮数を適切にシフトする必要があります。また、fを右にシフトする場合、結果を最も近い整数に丸めるために、丸める必要がある場合があります。
eが24より大きい場合、シフトf左e-24ビット。結果としてfを返します。
eが-1未満の場合、浮動小数点数は-½とbetweenの間で、排他的です。結果として0を返します。
それ以外の場合は、fを24 -eビットだけシフトします。ただし、最初に丸めに必要なビットを保存します。 rをfを符号なし32ビット整数にキャストし、3224 ----(e)ビットだけ左にシフトした結果に設定します(同等に、8 +-だけ左にシフトします) eビット)。これにより、シフトアウトされるビットがf(下)になり、32ビットで「左調整」されるため、開始位置が固定されます。
シフトf右24 -eビット。
rが2未満の場合31、何もしません(これは切り捨てられます。シフトされたビットは切り捨てられます)。 rが2より大きい場合31、1をfに追加します(これは切り上げです)。 rが2の場合31、fの下位ビットをfに追加します。 (fが奇数の場合、1をfに追加します。2つの等しく近い値のうち、これは偶数値に丸められます。)fを返します。
// With the proviso that your compiler implementation uses
// the same number of bytes for an int as for a float:
// example float
float f = 1.234f;
// get address of float, cast as pointer to int, reference
int i = *((int *)&f);
// get address of int, cast as pointer to float, reference
float g = *((float *)&i);
printf("%f %f %08x\n",f,g,i);
float x = 43.133;
int y;
assert (sizeof x == sizeof y);
memcpy (&y, &x, sizeof x);
...
(意味的に)浮動小数点数を「整数」(signed int
またはint
)をこのように使用します。
最終的に整数型になる可能性がありますが、実際にはそれ自体が意味のある値ではなく、IEEE754のエンコーディングスペースへのインデックスにすぎません。
unsigned
intはビットパターンと整数値として二重の目的を果たしますが、int
はそうではないと主張するかもしれません。
また、符号付き整数のビット操作を伴う プラットフォームの問題 もあります。