Как получить знак, мантисса и показатель числа с плавающей запятой

У меня есть программа, которая работает на двух процессорах, одна из которых не поддерживает поддержку с плавающей запятой. Поэтому мне нужно выполнить вычисления с плавающей запятой, используя фиксированную точку в этом процессоре. Для этой цели я буду использовать библиотеку эмуляции с плавающей запятой.

Мне нужно сначала извлечь знаки, мантиссы и показатели чисел с плавающей запятой на процессоре, которые поддерживают плавающую точку. Итак, мой вопрос заключается в том, как я могу получить знак, мантисс и показатель числа чисел с плавающей точкой с одной точностью.

Следуя формату этого рисунка,

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;
}

Ответ 1

Я думаю, что лучше использовать союзы для выполнения бросков, это яснее.

#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

Ответ 2

Узнайте формат чисел с плавающей запятой, используемых в ЦП, которые непосредственно поддерживают плавающие точки и разбивают их на эти части. Наиболее распространенный формат IEEE-754.

В качестве альтернативы вы можете получить эти части с помощью нескольких специальных функций (double frexp(double value, int *exp); и double ldexp(double x, int exp);), как показано в этом ответе.

Другой вариант - использовать %a с printf().

Ответ 3

Мой совет - придерживаться правила 0, а не повторять, что уже делают стандартные библиотеки, если этого достаточно. Посмотрите на math.h(cmath в стандартном С++) и функции frexp, frexpf, frexpl, которые разбивают значение с плавающей запятой (double, float или long double) в своей значимой и экспоненциальной части. Чтобы извлечь знак из знака, вы можете использовать signbit, также в math.h/cmath или copysign (только С++ 11). Некоторые альтернативы с более легкой семантикой - modf и ilogb/scalbn, доступные в С++ 11; http://en.cppreference.com/w/cpp/numeric/math/logb сравнивает их, но я не нашел в документации, как все эти функции ведут себя с +/- inf и NaN. Наконец, если вы действительно хотите использовать битмаски (например, вам отчаянно нужно знать точные биты, и ваша программа может иметь разные NaN с разными представлениями, и вы не доверяете вышеуказанным функциям), по крайней мере, сделать все независимым от платформы используя макросы в float.h/cfloat.

Ответ 4

Вы используете & неправильные биты. Я думаю, вы хотите:

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

Помните, что когда вы &, вы обнуляете биты, которые вы не устанавливаете. Таким образом, в этом случае вы хотите обнулить знаковый бит, когда вы получите экспонента, и вы хотите обнулить знаковый бит и экспонента, когда получите мантиссу.

Обратите внимание, что маски появляются непосредственно из вашего изображения. Итак, маска экспоненты будет выглядеть так:

0 11111111 00000000000000000000000

а маска мантиссы будет выглядеть так:

0 00000000 11111111111111111111111

Ответ 5

В пакете glibc-заголовков Linux содержится заголовок #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 Delight book is your friend. */

Ответ 7

Это прекрасно работает для 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;

Если вы попытаетесь распечатать их в двоичном/десятичном виде, вы получите следующее:

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

Ответ 8

Переместите указатель на переменную с плавающей запятой как нечто вроде unsigned int. Затем вы можете сдвигать и маскировать биты для получения каждого компонента.

float foo;
unsigned int ival, mantissa, exponent, sign;

foo = -21.4f;
ival = *((unsigned int *)&foo);
mantissa = ( ival & 0x7FFFFF);
ival = ival >> 23;
exponent = ( ival  & 0xFF );
ival = ival >> 8;
sign = ( ival & 0x01 );

Очевидно, вы, вероятно, не использовали бы неподписанные ints для показателя экспоненты и знака, но это должно по крайней мере дать вам эту идею.