Ответ 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);
Учитывая 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
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);
(Кто-то должен дважды проверить этот ответ, особенно пограничные случаи и округление отрицательных значений. Кроме того, я написал его для округления до ближайшего. Чтобы воспроизвести преобразование 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.
&x
указывает адрес x, поэтому имеет тип float*
.
(int*)&x
введите указатель на указатель на int
, т.е. на объект int*
.
*(int*)&x
разыменовать этот указатель на значение int
. Он не будет делать то, что вы считаете на машинах, где int
и float
имеют разные размеры.
И могут быть проблемы с контентом.
Это решение использовалось в алгоритме быстрого обратного квадратного корня.
Вы не можете (значимо) преобразовать число с плавающей запятой в "целое число" (signed int
или int
) таким образом.
Он может иметь тип integer, но на самом деле это просто индекс в пространстве кодирования IEEE754, а не значимое значение сам по себе.
Вы можете утверждать, что unsigned
int выполняет двоякую функцию как битовый шаблон и целочисленное значение, но int
не делает.
Также есть проблемы с платформой с бит-манипуляцией подписанных ints.
float x = 43.133;
int y;
assert (sizeof x == sizeof y);
memcpy (&y, &x, sizeof x);
...
Я не знаю, является ли это лучшей практикой, но я обнаружил, что это работает на моем компьютере.
float f = 12.123;
int i = *((int*)(&f));
Вы можете использовать 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 &'