Целочисленный логарифм Base-2
Вот что я делаю для 64-битных целых чисел без знака. Это вычисляет пол логарифма base-2, который эквивалентен индексу старшего значащего бита. Этот метод быстро курящий для больших чисел, потому что он использует развернутый цикл, который всегда выполняется в log₂64 = 6 шагов.
По сути, он вычитает постепенно уменьшающиеся квадраты в последовательности {0 ≤ k ≤ 5: 2 ^ (2 ^ k)} = {2³², 2¹⁶, 2⁸, 2⁴, 2², 2¹} = {4294967296, 65536, 256, 16, 4, 2, 1} и суммирует показатели k вычтенных значений.
int uint64_log2(uint64_t n)
{
#define S(k) if (n >= (UINT64_C(1) << k)) { i += k; n >>= k; }
int i = -(n == 0); S(32); S(16); S(8); S(4); S(2); S(1); return i;
#undef S
}
Обратите внимание, что возвращается -1, если задан неверный ввод 0 (именно это проверяет начальный -(n == 0)
). Если вы никогда не ожидаете вызвать его с помощью n == 0
, вы можете заменить int i = 0;
инициализатором и добавить assert(n != 0);
при входе в функцию.
Целочисленный логарифм Base-10
Целочисленные логарифмы Base-10 можно рассчитать, используя аналогично - с наибольшим квадратом для теста, равным 10¹⁶, потому что log₁₀2⁶⁴ ≅ 19.2659 ...
int uint64_log10(uint64_t n)
{
#define S(k, m) if (n >= UINT64_C(m)) { i += k; n /= UINT64_C(m); }
int i = -(n == 0);
S(16,10000000000000000); S(8,100000000); S(4,10000); S(2,100); S(1,10);
return i;
#undef S
}