Note de l'éditeur : c'est no ce que font réellement les compilateurs, et donne une mauvaise réponse pour les grands nombres entiers positifs se terminant par 9, en commençant par div10(1073741829) = 107374183
pas 107374182. Il est exact pour les entrées plus petites, cependant, ce qui peut être suffisant pour certaines utilisations.
Les compilateurs (y compris MSVC) utilisent des inverses multiplicatives en virgule fixe pour les diviseurs constants, mais ils utilisent une constante magique différente et un décalage sur le résultat de la moitié supérieure pour obtenir un résultat exact pour toutes les entrées possibles, ce qui correspond à ce que la machine abstraite C exige. Voir Article de Granlund & Montgomery sur l'algorithme.
Ver Pourquoi GCC utilise-t-il la multiplication par un nombre étrange pour implémenter la division d'un nombre entier ? pour des exemples de l'asm x86 que font gcc, clang, MSVC, ICC et d'autres compilateurs modernes.
Il s'agit d'une approximation rapide qui est inexacte pour les entrées importantes.
C'est même plus rapide que la division exacte via multiplier + décalage à droite que les compilateurs utilisent.
Vous pouvez utiliser la moitié supérieure du résultat d'une multiplication pour les divisions par de petites constantes intégrales. Supposons une machine 32 bits (le code peut être adapté en conséquence) :
int32_t div10(int32_t dividend)
{
int64_t invDivisor = 0x1999999A;
return (int32_t) ((invDivisor * dividend) >> 32);
}
Ce qui se passe ici, c'est que nous multiplions par une approximation proche de 1/10 * 2^32 et ensuite nous enlevons les 2^32. Cette approche peut être adaptée à différents diviseurs et différentes largeurs de bits.
Cela fonctionne très bien pour l'architecture ia32, puisque son instruction IMUL placera le produit 64 bits dans edx:eax, et la valeur de edx sera la valeur voulue. Viz (en supposant que le dividende est passé dans eax et le quotient retourné dans eax)
div10 proc
mov edx,1999999Ah ; load 1/10 * 2^32
imul eax ; edx:eax = dividend / 10 * 2 ^32
mov eax,edx ; eax = dividend / 10
ret
endp
Même sur une machine avec une instruction de multiplication lente, cela sera plus rapide qu'une division logicielle ou même matérielle.
0 votes
C'est possible (une soustraction répétée est une division), mais la question est de savoir si c'est plus rapide que la division lente.
0 votes
@esnyder. Désolé, je n'arrive pas à vous comprendre. Parlez-vous en base 17 ou en base 22 ?
0 votes
Base large deux. Le décalage vers la droite divise par 2^n ce qui résoudrait votre question si par "10" vous voulez dire 16 décimal ou 10h.
0 votes
Vous vous disputez avec moi ? J'essaie en fait d'admettre que I J'ai oublié de mentionner que ma réponse n'était pas pour la décimale.... C'est peut-être un peu obscur, mais c'était mon intention.
0 votes
@Thomas O - voir mon commentaire. Je ne remarque pas d'upvote....
1 votes
@esynder, Oui, je suppose que je me disputais avec vous, sur l'interprétation de 10(base 10) comme 10(base 16). Je pense qu'une telle interprétation par défaut est inhabituelle, au mieux.
0 votes
En rapport : Pourquoi GCC utilise-t-il la multiplication par un nombre étrange pour implémenter la division d'un nombre entier ? : Si vous disposez d'un multiplicateur rapide, vous pouvez diviser par des constantes de compilation avec juste une multiplication et un décalage de la moitié haute, en obtenant le résultat correct pour chaque dividende (contrairement à la réponse acceptée actuellement).