Подтвердить что ты не робот

Приведение float в int (побитовое) в C

Учитывая 32 бита, которые представляют число с плавающей запятой IEEE 754, как можно преобразовать число в целое число, используя целочисленные или битовые операции над представлением (вместо использования машинной команды или операции компилятора для преобразования)?

ИЗМЕНИТЬ № 1:

Мне нужно выполнить следующую функцию, но в некоторых случаях она терпит неудачу:

Вход: int x (содержит 32-битное число одиночной точности в формате IEEE 754)

  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;

ИЗМЕНИТЬ № 2:

Также нужна помощь с помощью https://cs.stackexchange.com/info/3484/converting-function-to-bitwise-only

4b9b3361

Ответ 1

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

Ответ 2

(Кто-то должен дважды проверить этот ответ, особенно пограничные случаи и округление отрицательных значений. Кроме того, я написал его для округления до ближайшего. Чтобы воспроизвести преобразование Cs, это должно быть изменено на round-to-zero. )

По существу, процесс:

Отделить 32 бита в один знаковый бит (ы), восемь битов экспоненты (e) и 23 значащих бита (f). Мы будем рассматривать их как целые числа с двумя дополнениями.

Если e равно 255, объект с плавающей запятой либо бесконечен (если f равен нулю), либо NaN (в противном случае). В этом случае преобразование невозможно выполнить и сообщить об ошибке.

В противном случае, если e не равно нулю, добавьте 2 f 24 в f. (Если e не равно нулю, то у значимого неявно есть 1 бит в начале. Добавление 2 24 делает этот бит явным в f.)

Вычесть 127 из e. (Это преобразует экспоненту из ее предвзятой/закодированной формы в фактический показатель. Если бы мы делали общее преобразование в любое значение, нам пришлось бы обрабатывать частный случай, когда e равно нулю: вычесть 126 вместо 127. Но, поскольку мы только преобразуются в целочисленный результат, мы можем пренебречь этим случаем, пока целочисленный результат равен нулю для этих маленьких входных чисел.)

Если s равно 0 (знак положительный), а e равно 31 или более, то значение переполняет подписанное 32-разрядное целое число (это 2 31 или больше). Невозможно выполнить преобразование и сообщить об ошибке.

Если s равно 1 (знак отрицательный), а e больше 31, тогда значение переполняет подписанное 32-разрядное целое число (оно меньше или равно -2 32). Если s равно единице, e равно 32, а f больше 2 24 (любой из исходных значащих бит был установлен), тогда значение переполняет 32-битное целое число (оно меньше - 2 31 если исходный f был равен нулю, он был бы точно -2 31 который не переполняется). В любом из этих случаев преобразование невозможно выполнить, и следует сообщить об ошибке.

Теперь у нас есть s, an 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, число с плавающей запятой составляет от -½ до ½, исключая. В результате получится 0.

В противном случае мы сдвинем f 24-24 бита. Однако мы сначала сохраним бит, который нам нужен для округления. Установите r в результат отливки f в неподписанное 32-битное целое число и сдвиньте его влево на 32- (24-е) биты (что эквивалентно, осталось 8 + e бит). Это принимает биты, которые будут сдвинуты из f (ниже) и "левая настройка" их в 32 битах, поэтому мы фиксируем положение, в котором они начинаются.

Сдвиньте правые 24-битные биты.

Если r меньше 2 31 ничего не делайте (это округление вниз, усеченные биты сдвига). Если r больше 2 31 добавьте его в f (это округление). Если r равно 2 31 добавьте младший бит f в f. (Если f нечетно, добавьте один в f. Из двух одинаково близких значений это округляет до четного значения.) Возврат f.

Ответ 3

&x указывает адрес x, поэтому имеет тип float*.

(int*)&x введите указатель на указатель на int, т.е. на объект int*.

*(int*)&x разыменовать этот указатель на значение int. Он не будет делать то, что вы считаете на машинах, где int и float имеют разные размеры.

И могут быть проблемы с контентом.

Это решение использовалось в алгоритме быстрого обратного квадратного корня.

Ответ 4

Вы не можете (значимо) преобразовать число с плавающей запятой в "целое число" (signed int или int) таким образом.

Он может иметь тип integer, но на самом деле это просто индекс в пространстве кодирования IEEE754, а не значимое значение сам по себе.

Вы можете утверждать, что unsigned int выполняет двоякую функцию как битовый шаблон и целочисленное значение, но int не делает.


Также есть проблемы с платформой с бит-манипуляцией подписанных ints.

Ответ 5

float x = 43.133;
int y;

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

Ответ 6

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

float f = 12.123;
int i = *((int*)(&f));

Ответ 7

Вы можете использовать float с помощью ссылки. Приведение такого типа никогда не должно генерировать код.

C++

float f = 1.0f;
int i = (int &)f;
printf("Float %f is 0x%08x\n", f, i);

Вывод:

Float 1.000000 is 0x3f800000

Если вы хотите, чтобы приведение стиля в стиле С++ использовало reinterpret_cast, например.

int i = reinterpret_cast<int &>(f);

Он не работает с выражениями, вам нужно сохранить его в переменной.

    int i_times_two;
    float f_times_two = f * 2.0f;
    i_times_two = (int &)f_times_two;

    i_times_two = (int &)(f * 2.0f);
main.cpp:25:13: error: C-style cast from rvalue to reference type 'int &'