Comment écrire la base de journal (2) en c / c ++


98

Existe-t-il un moyen d'écrire la fonction de journal (base 2)?

Le langage C a 2 fonctions intégrées - >>

1. logqui est la base e.

2. log10base 10;

Mais j'ai besoin de la fonction de journal de la base 2.Comment calculer cela.


1
Pour les calculs du globe oculaire, le logarithme de base 2 est presque égal au logarithme de base 10 plus le logarithme naturel. De toute évidence, il est préférable d'écrire une version plus précise (et plus rapide) dans un programme.
David Thornley

Pour les entiers, vous pouvez effectuer une boucle sur le décalage de bits à droite et vous arrêter lorsque vous atteignez 0. Le nombre de boucles est une approximation du journal
Basile Starynkevitch

Réponses:


198

Mathématiques simples:

    log 2 ( x ) = log y ( x ) / log y (2)

y peut être n'importe quoi, ce qui pour les fonctions de journal standard est soit 10, soit e .



53

Si vous recherchez un résultat intégral, vous pouvez simplement déterminer le bit le plus élevé défini dans la valeur et renvoyer sa position.


27
Il existe également une méthode intéressante pour cela (tirée de la Integer.highestOneBit(int)méthode de Java ):i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); i |= (i >> 8); i |= (i >> 16); return i - (i >>> 1);
Joey

38
... ouwhile (i >>= 1) { ++l; }
Lee Daniel Crocker

2
@Joey Cela fonctionnerait en supposant que l'entier soit large de 32 bits, non? Pour 64 bits, il y aurait un supplément i>>32. Mais comme Java n'a que des entiers 32 bits, c'est bien. Pour C / C ++, il doit être pris en compte.
Zoso

43
#define M_LOG2E 1.44269504088896340736 // log2(e)

inline long double log2(const long double x){
    return log(x) * M_LOG2E;
}

(la multiplication peut être plus rapide que la division)


1
Je voulais juste clarifier - en utilisant les règles de conversion du journal + le fait que log_2 (e) = 1 / log_e (2) -> nous obtenons le résultat
Guy L


9

Comme indiqué sur http://en.wikipedia.org/wiki/Logarithm :

logb(x) = logk(x) / logk(b)

Ce qui signifie que:

log2(x) = log10(x) / log10(2)

11
Notez que vous pouvez précalculer log10 (2) pour augmenter les performances.
corsiKa

@Johannes: Je doute que le compilateur pré-calcule log10 (2). Le compilateur ne sait pas que log10 renverra la même valeur à chaque fois. Pour tout ce que le compilateur sait, log10 (2) pourrait renvoyer des valeurs différentes lors d'appels successifs.
abelenky

@abelenky: Ok, je reprends ça. Puisque le compilateur ne voit jamais la source de l' log()implémentation, il ne le fera pas. Ma faute.
Joey

3
@abelenky: Puisqu'une log10()fonction est définie dans le standard C, le compilateur est libre de la traiter "spécialement", y compris le précalcul du résultat, ce qui, je crois, était la suggestion de @Johannes?
caf

1
@CarlNorum: Je viens de vérifier, et gcc 4.7 au moins remplace log10(2)par une constante.
caf

8

Si vous voulez le rendre rapide, vous pouvez utiliser une table de recherche comme dans Bit Twiddling Hacks (entiers log2 uniquement).

uint32_t v; // find the log base 2 of 32-bit v
int r;      // result goes here

static const int MultiplyDeBruijnBitPosition[32] = 
{
  0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
  8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
};

v |= v >> 1; // first round down to one less than a power of 2 
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;

r = MultiplyDeBruijnBitPosition[(uint32_t)(v * 0x07C4ACDDU) >> 27];

De plus, vous devriez jeter un œil aux méthodes intégrées de vos compilateurs, comme celle _BitScanReversequi pourrait être plus rapide car elle peut être entièrement calculée en matériel.

Jetez également un œil aux doublons possibles Comment faire un entier log2 () en C ++?


Pourquoi la multiplication et la recherche de table à la fin? Ne pourriez-vous pas simplement faire (v + 1) qui arrondirait à la prochaine puissance de deux? Et puis, vous pouvez passer à droite par un pour arrondir à la puissance suivante de 2.
Safayet Ahmed

@SafayetAhmed Veuillez décrire comment vous voulez trouver le log2 d'un nombre avec cette méthode. Je ne connais pas de moyen plus simple d'obtenir cette valeur. En plus d'utiliser l'arithmétique ci-dessus avec la table de recherche, on peut utiliser un algorithme itératif / récursif ou utiliser du matériel dédié / intégré pour faire le calcul.
bkausbk

Supposons que les bits d'une variable de 32 bits v soient numérotés de 0 (LSB) à N (MSB). Supposons que le bit d'ensemble le plus significatif de v soit n. Serait-il correct de dire que n représente le plancher (log2 (v))? N'êtes-vous pas intéressé à trouver simplement n donné v?
Safayet Ahmed

J'ai réalisé que ce que je décrivais vous donnerait simplement la puissance la plus basse la plus proche de 2 et non le logarithme réel. La multiplication et la recherche de table permettent de passer de la puissance de deux au logarithme. Vous déplacez le nombre 0x07C4ACDD vers la gauche d'un certain montant. La quantité de décalage vers la gauche dépendra de la puissance de deux. Le nombre est tel que toute séquence consécutive de 5 bits est unique. (0000 0111 1100 0100 0110 1100 1101 1101) vous donne les séquences (00000) (00001) ... (11101). En fonction de votre décalage vers la gauche, vous obtenez l'un de ces modèles de 5 bits. Puis recherche de table. Très agréable.
Safayet Ahmed

3
log2(x) = log10(x) / log10(2)

Voter pour la simplicité, la clarté et fournir un code basé sur les informations fournies par l'OP.
yoyo le

3
uint16_t log2(uint32_t n) {//but truncated
     if (n==0) throw ...
     uint16_t logValue = -1;
     while (n) {//
         logValue++;
         n >>= 1;
     }
     return logValue;
 }

Fondamentalement, le même que celui de tomlogic .


1
Il y a quelques problèmes avec cette solution, mais en général, c'est bien si vous voulez éviter les virgules flottantes. Vous comptez sur le débordement pour que cela fonctionne puisque vous initialisez un entier non signé avec -1. Cela peut être résolu en l'initialisant à 0, puis en renvoyant la valeur - 1, en supposant que vous vérifiez le cas 0, ce que vous faites. L'autre problème est que vous comptez sur l'arrêt de la boucle lorsque n == 0, ce que vous devez indiquer explicitement. À part cela, c'est génial si vous voulez éviter les virgules flottantes.
Rian Quinn

2

Vous devez inclure math.h (C) ou cmath (C ++) Bien sûr, gardez à l'esprit que vous devez suivre les mathématiques que nous connaissons ... seuls les nombres> 0.

Exemple:

#include <iostream>
#include <cmath>
using namespace std;

int main(){
    cout<<log2(number);
}

2

J'avais besoin d'avoir plus de précision que la position du bit le plus significatif, et le microcontrôleur que j'utilisais n'avait pas de bibliothèque mathématique. J'ai trouvé que simplement utiliser une approximation linéaire entre 2 ^ n valeurs pour les arguments de valeur entière positive fonctionnait bien. Voici le code:

uint16_t approx_log_base_2_N_times_256(uint16_t n)
{
    uint16_t msb_only = 0x8000;
    uint16_t exp = 15;

    if (n == 0)
        return (-1);
    while ((n & msb_only) == 0) {
        msb_only >>= 1;
        exp--;
    }

    return (((uint16_t)((((uint32_t) (n ^ msb_only)) << 8) / msb_only)) | (exp << 8));
}

Dans mon programme principal, j'avais besoin de calculer N * log2 (N) / 2 avec un résultat entier:

temp = (((uint32_t) N) * approx_log_base_2_N_times_256) / 512;

et toutes les valeurs 16 bits n'ont jamais été décalées de plus de 2%


1

Toutes les réponses ci-dessus sont correctes. Cette réponse ci-dessous peut être utile si quelqu'un en a besoin. J'ai vu cette exigence dans de nombreuses questions que nous résolvons en utilisant C.

log2 (x) = logy (x) / logy (2)

Cependant, si vous utilisez le langage C et que vous voulez que le résultat soit un entier, vous pouvez utiliser ce qui suit:

int result = (int)(floor(log(x) / log(2))) + 1;

J'espère que cela t'aides.


0

Consultez vos cours de mathématiques de base, log n / log 2. Peu importe que vous choisissiez logou log10dans ce cas, diviser par logla nouvelle base fait l'affaire.


0

Version améliorée de ce qu'a fait Ustaman Sangat

static inline uint64_t
log2(uint64_t n)
{
    uint64_t val;
    for (val = 0; n > 1; val++, n >>= 1);

    return val;
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.