web-dev-qa-db-ja.com

浮動小数点数の符号、仮数、指数を取得する方法

2つのプロセッサで実行されているプログラムがあり、そのうちの1つは浮動小数点をサポートしていません。そのため、そのプロセッサの固定小数点を使用して浮動小数点計算を実行する必要があります。そのために、浮動小数点エミュレーションライブラリを使用します。

最初に、浮動小数点をサポートするプロセッサ上の浮動小数点数の符号、仮数、指数を抽出する必要があります。したがって、私の質問は、単精度浮動小数点数の符号、仮数、指数をどのように取得できるかです。

この図のフォーマットに従って、

enter image description here これは私がこれまでやったことですが、符号を除いて、仮数も指数も正しくありません。私は何かを逃していると思う。

void getSME( int& s, int& m, int& e, float number )
{
    unsigned int* ptr = (unsigned int*)&number;

    s = *ptr >> 31;
    e = *ptr & 0x7f800000;
    e >>= 23;
    m = *ptr & 0x007fffff;
}
38
MetallicPriest

キャストを行うにはユニオンを使用する方が良いと思いますが、より明確です。

#include <stdio.h>

typedef union {
  float f;
  struct {
    unsigned int mantisa : 23;
    unsigned int exponent : 8;
    unsigned int sign : 1;
  } parts;
} float_cast;

int main(void) {
  float_cast d1 = { .f = 0.15625 };
  printf("sign = %x\n", d1.parts.sign);
  printf("exponent = %x\n", d1.parts.exponent);
  printf("mantisa = %x\n", d1.parts.mantisa);
}

http://en.wikipedia.org/wiki/Single_precision に基づく例

21
eran

私のアドバイスは、これが十分であれば、標準ライブラリがすでに行っていることをやり直すのではなく、ルール0に固執することです。仮数部と指数部で浮動小数点値(double、float、long double)を分割するmath.h(標準C++のcmath)と関数frexp、frexpf、frexplを見てください。仮数から記号を抽出するには、math.h/cmathまたはcopysign(C++ 11のみ)でもsignbitを使用できます。セマンティクスがわずかに異なるいくつかの選択肢は、C++ 11で利用可能なmodfとilogb/scalbnです。 http://en.cppreference.com/w/cpp/numeric/math/logb はそれらを比較しますが、これらのすべての関数が+/- infおよびNaNでどのように動作するかをドキュメントで見つけませんでした。最後に、本当にビットマスクを使用したい場合(たとえば、正確なビットを必死に知る必要があり、プログラムには異なる表現の異なるNaNがあり、上記の関数を信頼していない場合)、少なくともすべてをプラットフォームに依存しないようにしますfloat.h/cfloatのマクロを使用します。

24
Pietro Braione

浮動小数点を直接サポートするCPUで使用される浮動小数点数の形式を見つけ、それらの部分に分けます。最も一般的な形式は IEEE-754 です。

または、 this answer に示すように、いくつかの特別な関数(double frexp(double value, int *exp);およびdouble ldexp(double x, int exp);)を使用してこれらのパーツを取得できます。

別のオプション は、printf()とともに%aを使用することです。

20
Alexey Frunze

あなたは&ing間違ったビット。あなたが欲しいと思う:

s = *ptr >> 31;
e = *ptr & 0x7f800000;
e >>= 23;
m = *ptr & 0x007fffff;

&、設定していないビットをゼロにしています。したがって、この場合、指数を取得するときに符号ビットをゼロにし、仮数を取得するときに符号ビットと指数をゼロにします。

マスクは画像から直接取得されることに注意してください。したがって、指数マスクは次のようになります。

0 11111111 00000000000000000000000

仮数マスクは次のようになります。

0 00000000 11111111111111111111111111

7
Xymostech

Linuxパッケージでは、glibc-headersはヘッダー#include <ieee754.h>浮動小数点型の定義、たとえば:

union ieee754_double
  {
    double d;

    /* This is the IEEE 754 double-precision format.  */
    struct
      {
#if __BYTE_ORDER == __BIG_ENDIAN
    unsigned int negative:1;
    unsigned int exponent:11;
    /* Together these comprise the mantissa.  */
    unsigned int mantissa0:20;
    unsigned int mantissa1:32;
#endif              /* Big endian.  */
#if __BYTE_ORDER == __LITTLE_ENDIAN
# if    __FLOAT_Word_ORDER == __BIG_ENDIAN
    unsigned int mantissa0:20;
    unsigned int exponent:11;
    unsigned int negative:1;
    unsigned int mantissa1:32;
# else
    /* Together these comprise the mantissa.  */
    unsigned int mantissa1:32;
    unsigned int mantissa0:20;
    unsigned int exponent:11;
    unsigned int negative:1;
# endif
#endif              /* Little endian.  */
      } ieee;

    /* This format makes it easier to see if a NaN is a signalling NaN.  */
    struct
      {
#if __BYTE_ORDER == __BIG_ENDIAN
    unsigned int negative:1;
    unsigned int exponent:11;
    unsigned int quiet_nan:1;
    /* Together these comprise the mantissa.  */
    unsigned int mantissa0:19;
    unsigned int mantissa1:32;
#else
# if    __FLOAT_Word_ORDER == __BIG_ENDIAN
    unsigned int mantissa0:19;
    unsigned int quiet_nan:1;
    unsigned int exponent:11;
    unsigned int negative:1;
    unsigned int mantissa1:32;
# else
    /* Together these comprise the mantissa.  */
    unsigned int mantissa1:32;
    unsigned int mantissa0:19;
    unsigned int quiet_nan:1;
    unsigned int exponent:11;
    unsigned int negative:1;
# endif
#endif
      } ieee_nan;
  };

#define IEEE754_DOUBLE_BIAS 0x3ff /* Added to exponent.  */
6
  1. 複数のことを行う関数を作成しないでください。
  2. マスクしてからシフトしないでください。シフトしてからマスクします。
  3. 遅く、キャッシュを破壊し、エラーが発生しやすいため、不必要に値を変更しないでください。
  4. マジックナンバーを使用しないでください。
/* NaNs, infinities, denormals unhandled */
/* assumes sizeof(float) == 4 and uses ieee754 binary32 format */
/* assumes two's-complement machine */
/* C99 */
#include <stdint.h>

#define SIGN(f) (((f) <= -0.0) ? 1 : 0)

#define AS_U32(f) (*(const uint32_t*)&(f))
#define FLOAT_EXPONENT_WIDTH 8
#define FLOAT_MANTISSA_WIDTH 23
#define FLOAT_BIAS ((1<<(FLOAT_EXPONENT_WIDTH-1))-1) /* 2^(e-1)-1 */
#define MASK(width)  ((1<<(width))-1) /* 2^w - 1 */
#define FLOAT_IMPLICIT_MANTISSA_BIT (1<<FLOAT_MANTISSA_WIDTH)

/* correct exponent with bias removed */
int float_exponent(float f) {
  return (int)((AS_U32(f) >> FLOAT_MANTISSA_WIDTH) & MASK(FLOAT_EXPONENT_WIDTH)) - FLOAT_BIAS;
}

/* of non-zero, normal floats only */
int float_mantissa(float f) {
  return (int)(AS_U32(f) & MASK(FLOAT_MANTISSA_BITS)) | FLOAT_IMPLICIT_MANTISSA_BIT;
}

/* Hacker's Delight book is your friend. */
0
user246672

これはdoubleデータ型に対して正常に機能します。

double n = 923842342.3423452;

int  sign       = *(uintmax_t *)&n >> 63;
int  exp        = (*(uintmax_t *)&n << 1 >> 53) - BIAS; /* i.e 1023 */
long mantissa   = *(uintmax_t *)&n << 12 >> 12;

バイナリ/ 10進数で印刷しようとすると、次のようになります。

In Binary:   0  0b00000011101  0b1011100010000101101110010011001010111101000111111000
In Decimal:  0             29                                        3246151636341240
0
AymenTM