Как эффективно сравнить знак двух значений с плавающей точкой при обработке отрицательных нулей
Учитывая два числа с плавающей точкой, я ищуэффективное способ проверить, имеют ли они одинаковый знак,учитывая, что если любое из двух значений равно нулю (+0.0 или -0.0), они должны иметь одинаковый знак.
Например,
SameSign (1.0, 2.0) должен вернуть trueSameSign (-1.0, -2.0) должен вернуть trueSameSign (-1.0, 2.0) должен вернуть falseSameSign (0.0, 1.0) должен вернуть trueSameSign (0.0, -1.0) должен вернуть trueSameSign (-0.0, 1.0) должен вернуть trueSameSign (-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
до трех значений.
Редактировать:
Я сделал несколько измерений производительности на трех вариантах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 тиков