web-dev-qa-db-ja.com

Cでfloatをint(ビット単位)にキャストする

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;
14
Anonymous

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);
20
Carl

_&x_はxのアドレスを与えるため、_float*_タイプを持っています。

_(int*)&x_は、そのポインターをintへのポインターに、つまり_int*_モノにキャストします。

*(int*)&xは、そのポインタをint値に逆参照します。 intfloatのサイズが異なるマシンでは、信じられないことを行います。

そしてエンディアンの問題があるかもしれません。

このソリューションは 高速逆平方根 アルゴリズムで使用されました。

10

(誰かがこの答え、特に境界ケースと負の値の丸めを再確認する必要があります。また、最も近い値に丸めるためにそれを書きました。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、オーバーフローしない)。これらのいずれの場合でも、変換は実行できず、エラーが報告されます。

これで、オーバーフローしない値のse、およびfが得られたので、最終的な値を準備できます。

sが1の場合、fを-fに設定します。

指数値は、1(両端を含む)から2(両端を含まない)までの仮数に対するものですが、仮数は2のビットで始まります24。ですから、それに合わせて調整する必要があります。 eが24の場合、有効桁数は正しいので完了です。結果としてfを返します。 eが24より大きいか24未満の場合、仮数を適切にシフトする必要があります。また、fを右にシフトする場合、結果を最も近い整数に丸めるために、丸める必要がある場合があります。

eが24より大きい場合、シフトfe-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の場合31fの下位ビットをfに追加します。 (fが奇数の場合、1をfに追加します。2つの等しく近い値のうち、これは偶数値に丸められます。)fを返します。

7
// 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);
3
Dino Dini
float x = 43.133;
int y;

assert (sizeof x == sizeof y);
memcpy (&y, &x, sizeof x);
...
1
wildplasser

(意味的に)浮動小数点数を「整数」(signed intまたはint)をこのように使用します。

最終的に整数型になる可能性がありますが、実際にはそれ自体が意味のある値ではなく、IEEE754のエンコーディングスペースへのインデックスにすぎません。

unsigned intはビットパターンと整数値として二重の目的を果たしますが、intはそうではないと主張するかもしれません。


また、符号付き整数のビット操作を伴う プラットフォームの問題 もあります。

0
Alex Brown