Учитывая два числа с плавающей точкой, я ищу эффективный способ проверить, имеют ли они один и тот же знак, , учитывая, что если любое из двух значений равно нулю (+0,0 или -0.0), они должны иметь одинаковый знак .
Например,
- SameSign (1.0, 2.0) должен возвращать true
- SameSign (-1.0, -2.0) должен возвращать true
- SameSign (-1.0, 2.0) должен возвращать false
- SameSign (0.0, 1.0) должен возвращать true
- SameSign (0.0, -1.0) должен возвращать true
- SameSign (-0,0, 1,0) должен возвращать true
- SameSign (-0.0, -1.0) должен возвращать true
Наивная, но правильная реализация SameSign
в C ++ будет выглядеть так:
bool SameSign(float a, float b)
{
if (fabs(a) == 0.0f || fabs(b) == 0.0f)
return true;
return (a >= 0.0f) == (b >= 0.0f);
}
Предполагая модель с плавающей точкой IEEE, вот вариант SameSign
, который компилируется в код без ответвлений (по крайней мере, в Visual C ++ 2008):
bool SameSign(float a, float b)
{
int ia = binary_cast<int>(a);
int ib = binary_cast<int>(b);
int az = (ia & 0x7FFFFFFF) == 0;
int bz = (ib & 0x7FFFFFFF) == 0;
int ab = (ia ^ ib) >= 0;
return (az | bz | ab) != 0;
}
с binary_cast
определяется следующим образом:
template <typename Target, typename Source>
inline Target binary_cast(Source s)
{
union
{
Source m_source;
Target m_target;
} u;
u.m_source = s;
return u.m_target;
}
Я ищу две вещи:
Более быстрая и эффективная реализация SameSign
с использованием битовых трюков, трюков FPU или даже встроенных функций SSE.
Эффективное расширение SameSign
до трех значений .
Edit:
Я провел некоторые измерения производительности для трех вариантов SameSign
(два варианта, описанные в исходном вопросе, плюс вариант Стивена). Каждая функция выполнялась 200-400 раз на всех последовательных парах значений в массиве из 101 числа с плавающей запятой, заполненных случайным образом с -1,0, -0,0, +0,0 и +1,0. Каждое измерение повторялось 2000 раз, и минимальное время сохранялось (чтобы отсеять все эффекты кэша и замедления, вызванные системой). Код был скомпилирован с Visual C ++ 2008 SP1 с максимальной оптимизацией и включенным генерацией кода SSE2. Измерения проводились на Core 2 Duo P8600 2,4 ГГц.
Вот время, не считая накладных расходов на выборку входных значений из массива, вызов функции и получение результата (который составляет 6-7 тактов):
- Наивный вариант: 15 тиков
- Вариант битовой магии: 13 тиков
- вариант Стивенса: 6 тиков