Conversion entier rapide en décimal

Étant donné un entier (non signé), quel est le moyen généralement le plus rapide de le convertir en chaîne contenant sa représentation décimale?

La façon naïve de le faire est de diviser à plusieurs resockets par 10, jusqu’à atteindre zéro. Je n’aime pas cette approche, car

  • utilise la division entière, ce qui est à la fois lent et non disponible sur certaines plateformes intégrées
  • oblige le programmeur à retourner la chaîne par la suite. Cela double le nombre d’opérations de mémoire nécessaires.

J’ai pensé à la méthode suivante pour convertir des entiers en base décimale. Est-ce une bonne idée? Comment cela se fait-il dans les implémentations courantes de fonctions comme printf ?

 #include  const static uint64_t i64_tab[20] = { 1u, 10u, 100u, 1000u, 10000u, 100000u, /* 10^ 5 */ 1000000u, 10000000u, 100000000u, 1000000000u, 10000000000u, /* 10^10 */ 100000000000u, 1000000000000u, 10000000000000u, 100000000000000u, 1000000000000000u, /* 10^15 */ 10000000000000000u, 100000000000000000u, 1000000000000000000u, 10000000000000000000u /* 10^19 */ }; void uint64_to_ssortingng(char *out, uint64_t in) { int i; uint64_t tenpow; char accum; for (i = 19;i > 0;i--) { if (in >= i64_tab[i]) break; } do { tenpow = i64_tab[i]; accum = '0'; while (in >= tenpow) { in -= tenpow; accum++; } *out++ = accum; } while (i --> 0); *out = '\0'; } const static uint32_t i32_tab[10] = { 1u, 10u, 100u, 1000u, 10000u, 100000u, /* 10^ 5 */ 1000000u, 10000000u, 100000000u, 1000000000u, /* 10^9 */ }; void uint32_to_ssortingng(char *out, uint32_t in) { int i; uint32_t tenpow; char accum; for (i = 9;i > 0;i--) if (in >= i32_tab[i]) break; do { tenpow = i32_tab[i]; accum = '0'; while (in >= tenpow) { in -= tenpow; accum++; } *out++ = accum; } while (i --> 0); *out = '\0'; } 

L’approche la plus rapide sur tous les microcontrôleurs, sauf les plus simples (par exemple 8 bits), consiste à utiliser la division, mais à réduire le nombre de divisions en générant plusieurs chiffres à la fois.

Vous trouverez un code très optimisé dans les réponses à ma question ici . Son utilisation en C devrait être une modification sortingviale pour éliminer std::ssortingng – aucune fonctionnalité C ++ n’est utilisée dans la conversion réelle. Le kernel est

 while(val>=100) { int pos = val % 100; val /= 100; *(short*)(c-1)=*(short*)(digit_pairs+2*pos); // or use memcpy c-=2; } while(val>0) { *c--='0' + (val % 10); val /= 10; } 

J’ai également fourni un code optimisé sans division pour les micros 8 bits, similaire à l’idée présentée dans le code de la question, mais sans boucle. Il se termine avec beaucoup de code comme ceci:

  if (val >= 80) { ch |= '8'; val -= 80; } else if (val >= 40) { ch |= '4'; val -= 40; } if (val >= 20) { ch |= '2'; val -= 20; } if (val >= 10) { ch |= '1'; val -= 10; } 

Je crois que la division entière par une constante est aussi rapide que la multiplication, car le compilateur optimise la division entière en multiplication d’entiers pour les diviseurs constants. Ceci est une astuce mathématique difficile réalisée par la plupart des compilateurs optimistes.

Le moyen le plus rapide consiste généralement à indexer dans un assez grand nombre de pointeurs sur des chaînes. Une recherche de tableau, une déréférence de pointeur. Cependant, la consommation de mémoire est lourde … C’est la nature des compromis d’ingénierie. A quelle vitesse est assez rapide?

La version MS de printf le fait de manière “naïve” (après avoir configuré plusieurs variables basées sur les indicateurs optionnels):

  while (precision-- > 0 || number != 0) { digit = (int)(number % radix) + '0'; number /= radix; /* reduce number */ if (digit > '9') { /* a hex digit, make it a letter */ digit += hexadd; } *text.sz-- = (char)digit; /* store the digit */ }