Быстрая реализация log2 (float x) C ++ - PullRequest
7 голосов
/ 23 февраля 2012

Мне нужна очень быстрая реализация функции log2 (float x) в C ++.

Я нашел очень интересную реализацию (и очень быструю!)

#include <intrin.h>

inline unsigned long log2(int x)
{
    unsigned long y;
    _BitScanReverse(&y, x);
    return y;
}

Но эта функция хороша только для целочисленных значений на входе.

Вопрос: Можно ли преобразовать эту функцию во входную переменную типа double ?

UPD :

Я нашел эту реализацию:

typedef unsigned long uint32;
typedef long int32;   
static inline int32 ilog2(float x)
{
    uint32 ix = (uint32&)x;
    uint32 exp = (ix >> 23) & 0xFF;
    int32 log2 = int32(exp) - 127;

    return log2;
}

, что намного быстрее, чем в предыдущем примере, но вывод имеет тип без знака.

Можно ли заставить эту функцию возвращать тип double ?

Заранее спасибо!

Ответы [ 8 ]

6 голосов
/ 23 февраля 2012

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

Портативно:

#include <cmath>

int log2_fast(double d) {
    int result;
    std::frexp(d, &result);
    return result-1;
}

Возможно, быстрее, но полагается на неопределенныйи неопределенное поведение:

int log2_evil(double d) {
    return ((reinterpret_cast<unsigned long long&>(d) >> 52) & 0x7ff) - 1023;
}
5 голосов
/ 26 февраля 2015

MSVC + GCC-совместимая версия, которая дает XX.XXXXXXX + -0.0054545

float mFast_Log2(float val) {
    union { float val; int32_t x; } u = { val };
    register float log_2 = (float)(((u.x >> 23) & 255) - 128);              
    u.x   &= ~(255 << 23);
    u.x   += 127 << 23;
    log_2 += ((-0.3358287811f) * u.val + 2.0f) * u.val  -0.65871759316667f; 
    return (log_2);
} 
4 голосов
/ 22 мая 2012

Редактировать: См. Ссылку по вакансии в комментариях ниже для лучшей версии.


Функция быстрого журнала () (примерно в 5 раз быстрее)

Может быть, вам интересно.Код работает здесь;Это не бесконечно точно, хотя.Поскольку код поврежден на веб-странице (> был удален), я выложу его здесь:

inline float fast_log2 (float val)
{
   int * const    exp_ptr = reinterpret_cast <int *> (&val);
   int            x = *exp_ptr;
   const int      log_2 = ((x >> 23) & 255) - 128;
   x &= ~(255 << 23);
   x += 127 << 23;
   *exp_ptr = x;

   val = ((-1.0f/3) * val + 2) * val - 2.0f/3;   // (1)

   return (val + log_2);
} 

inline float fast_log (const float &val)
{
   return (fast_log2 (val) * 0.69314718f);
}
2 голосов
/ 14 марта 2015

C ++ 11 добавлено std :: log2 в <cmath>.

2 голосов
/ 23 февраля 2012

Вы можете взглянуть на эту реализацию , но:

  • может не работать на некоторых платформах
  • может не бить std :: log
0 голосов
/ 07 февраля 2018

Это улучшение первого ответа, которое не зависит от реализации IEEE, хотя я полагаю, что это быстро только на машинах IEEE, где frexp() является в основном бесплатной функцией.

Вместо отбрасывания дроби, возвращаемой frexp, можно использовать ее для линейной интерполяции. Значение дроби составляет от 0,5 до 1,0, если оно положительное, поэтому мы растягиваем значение от 0,0 до 1,0 и добавляем его к показателю степени.

На практике, похоже, что эта быстрая оценка хороша примерно до 5-10%, всегда возвращая значение, которое является немного низким. Я уверен, что это можно сделать лучше, настроив коэффициент масштабирования 2*.

#include <cmath>

double log2_fast(double d) {
    int exponent;
    double fraction = std::frexp(d, &exponent);
    return (result-1) + 2* (fraction - 0.5);
}

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

#include <cmath>

int main()
{
   for(double x=0.001;x<1000;x+=0.1)
   {
      std::cout << x << " " << std::log2(x) << " " << log2_fast(x) << "\n";
   }
}
0 голосов
/ 23 февраля 2012

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

0 голосов
/ 23 февраля 2012

Эта функция не C ++, она специфична для MSVC ++.Кроме того, я очень сомневаюсь, что такая внутренняя сущность существует.И если они это сделают, стандартная функция будет просто настроена на ее использование.Так что просто позвоните в стандартную библиотеку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...