Если вам действительно нужно
ceil(12 * log2(/* something */))
тогда есть очень простое вычисление O (1), которое будет работать, используя таблицу только из 12 значений.
Используйте frexp(), чтобы разделить значение на экспоненту и мантиссу. (Это всего лишь битовая манипуляция, так что это займет всего пару циклов.)
Найдите мантиссу в предварительно вычисленном списке степеней 12-го корня из 2,0 (деленного на 2), что вы можете сделать не более чем с четырьмя сравнениями.
Результат: 12*(показатель степени - 1) + индекс наименьшего корня >= мантисса.
Отредактировано, чтобы добавить:
На самом деле есть еще лучшее решение, потому что степени 12-го корня из двух распределены достаточно равномерно. Если вы разделите [0,5, 1,0) (диапазон мантиссы, возвращаемый frexp) на 17 равномерно расположенных поддиапазонов, каждый поддиапазон попадет в одно из двух возможных возвращаемых значений. Поэтому, если вы связываете каждый поддиапазон с индексом в векторе корней, вам нужно только сравнить цель (в пределах этого диапазона) с одним корнем.
Мне слишком поздно писать связный английский, поэтому вам придется довольствоваться кодом:
int Ceil12TimesLog2(double x) {
int exp;
double mantissa = std::frexp(x, &exp) * 2;
int idx = indexes[int((mantissa - 1.0) * 17)];
return 12 * (exp - 1) + (mantissa <= roots[idx] ? idx : idx + 1);
}
// Here are the reference tables.
double roots[12] = {
0x1.0000000000000p+0,
0x1.0f38f92d97963p+0,
0x1.1f59ac3c7d6c0p+0,
0x1.306fe0a31b715p+0,
0x1.428a2f98d728bp+0,
0x1.55b8108f0ec5ep+0,
0x1.6a09e667f3bccp+0,
0x1.7f910d768cfb0p+0,
0x1.965fea53d6e3dp+0,
0x1.ae89f995ad3adp+0,
0x1.c823e074ec129p+0,
0x1.e3437e7101344p+0
};
int indexes[17] = { 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11 };
Я попробовал это, и это сокращает общее время вычислений примерно с 0,5 секунды (для log2f) до примерно 0,15 секунды, используя цикл в OP. Общий размер справочных таблиц составляет 164 байта.
person
rici
schedule
25.10.2012