Сравнение поплавков с использованием побитовых операторов - PullRequest
0 голосов
/ 23 июня 2010

Как сделать сравнения значений с плавающей запятой, используя серии побитовых операций?

Ответы [ 9 ]

6 голосов
/ 23 июня 2010

Не. Просто ... не надо. Используйте == или его диких и дурацких соседей > и <. Есть даже сумасшедшие гибриды, <= и >=! Они должны охватывать все ваши потребности сравнения с плавающей запятой.

Обновление: Неважно, не используйте ==. Остальные должны быть в порядке.

Обновление обновления: Не использовать == означает, что вы, вероятно, не должны использовать <= или >=. Мораль этой истории в том, что поплавки хитрые, поэтому вам совершенно определенно не следует пытаться поразрядно их обрабатывать.

2 голосов
/ 25 октября 2011

В некоторых ситуациях имеет смысл поразрядно манипулировать числами с плавающей точкой. Например, если вы пытаетесь смоделировать аппаратное обеспечение с более низкой битовой точностью или большей. В этих случаях вам нужно будет получить доступ к битам числа с плавающей точкой. Как сказал bta, первое, что вы должны знать, это стандарт IEEE 754, чтобы вы знали, какими битами вы манипулируете.

Тогда вы можете использовать решение, такое как решение ShinTakezou, но я бы предложил что-то более изощренное.

Предположим, нам нужна одинарная точность.

Сначала мы объявляем структуру с различными полями с плавающей запятой:

typedef struct s_float{
    int sign : 1;
    int exponent : 8;
    int mantissa : 23;
}my_float_struct;

тогда мы объявляем объединение так:

union u_float{
    float the_float;
    my_float_struct the_structure;
}my_float;

тогда мы можем получить доступ к float в коде, выполнив:

my_float.the_float = <float number>;

битовые поля используются, например, следующим образом:

printf("%f is %d.%d.%d\n",my_float.the_float,my_float.the_structure.sign,my_float.the_structure.exponent,my_float.the_structure.mantissa);

И вы можете назначать, изменять поля или все, что вам нужно.

1 голос
/ 23 июня 2010

Я не думаю, что использование побитового оператора на float сделает то, что вы думаете, он сделает. Перед этим убедитесь, что вы знакомы со стандартом IEEE 754 , который определяет, как числа с плавающей запятой представляются внутри. Хотя это совершенно корректная операция, она, скорее всего, не очень полезна.

Что именно вы пытаетесь достичь? Вероятно, есть лучший способ сделать это.

0 голосов
/ 27 октября 2011

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

float real_value;
(int)real_value | 3, // this will work
real_value | 3; // This will not work
0 голосов
/ 23 июня 2010

Если вы внимательно прочитаете спецификации IEEE о числах fp, вы на старте. Следующий шаг - реализовать в программном обеспечении то, что уже сделано аппаратными средствами, если процессор имеет поддержку fp, в противном случае вы должны выполнять операции fp (в соответствии с IEEE) с нуля в программном обеспечении ... Прекрасно выполнимо оба (это было то, что нужно было) делать до того, как IEEE был использован в сопроцессорах cpus или fp, если IEEE 754 - все, что было нужно).

Давайте предположим, что ваша цель не может fp вообще, поэтому вам нужно реализовать это с нуля. Тогда вам решать, как сохранить номер в памяти (вы можете согласиться с порядком номера вашей системы или нет); например float для 1.23 сохраняется в mem как 0xA4709D3F на моей машине (LE), и на самом деле «правильный» путь - 0x3F9D70A4 (наш способ записи больше похож на BE, чем на LE, но «правильного» способа нет ... даже хотя этот способ позволяет нам проверять данные непосредственно с помощью спецификаций, поэтому, если я напишу -1.23, я получу 0xBF9D70A4, где ясно, что бит знака повышен до 1)

Но так как мы собираемся реализовать это с нуля, мы можем записать число в память следующим образом:

unsigned char p[4];
p[0] = 0x3f; p[1] = 0x9d; p[2] = 0x70; p[3] = 0xa4;

а потом наступает сложная часть ... Вот так:

bool is_positive(float_t *p)
{
  return ! (p[0] & 0x80); // or alike
}

Мы работаем в памяти, предполагая, что наш proc не способен обрабатывать 32-битные (или более) целые числа. Конечно, я выбрал более простую операцию ...! Остальные сложнее, но, начиная с описания IEEE 754 и рассуждая, вы можете реализовать то, что хотите. Как видите, это не так просто ... Где-то вы могли найти библиотеки, которые реализуют операции с числами fp, когда нет единицы с плавающей запятой, но теперь я не смог найти ни одной (например, Amiga mathieeedoubbas.library , но я думаю, что вы не можете найти источники для этого, и в любом случае это может быть непосредственно в m68k asm ...; просто сказать, что программный импл может где-то существовать ...)

0 голосов
/ 23 июня 2010

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

http://www.stereopsis.com/radix.html

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

0 голосов
/ 23 июня 2010

В действительно редких случаях, когда вам необходимо это сделать (например, при работе с иностранным форматом с плавающей запятой, например данными в формате VAX G, которыми манипулируют на ПК), вы обычно делаете это, помещая данные с плавающей запятой вцелое число того же размера или в массиве char (опять же, правильного размера).

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

0 голосов
/ 23 июня 2010

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

0 голосов
/ 23 июня 2010

Домен, представленный float числами, не подходит для их битовой реализации и манипулирования.

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

Какой смысл будет иметь ИЛИ для двух экспонент или XOR для двух мантисс?Я имею в виду в практических операциях с плавающей точкой ..

...