Diviser par 10 à l'aide de bits des changements?
Est-il possible de diviser un entier non-signé par 10 par le biais de la pure bits quarts de travail, l'addition, la soustraction et peut-être se multiplier? À l'aide d'un processeur avec des ressources très limitées et lent diviser.
- Il est possible (soustraction répétée de la division), mais la question est de savoir si c'est plus vite que la lenteur de la division.
- Désolé, je ne peux pas vous comprendre. Parlez-vous dans la base de 17 ou de la base de 22?
- De la Base de deux. Décalage à droite divise par 2^n qui permettrait de résoudre votre question si par "10", tu veux dire 16 décimal ou 10h.
- Êtes-vous disputer avec moi? Je suis en train d'essayer d'admettre que je n'ai pas parler de ma réponse n'était pas pour les décimales.... Peut-être un peu obscur, mais que c'était mon intention.
- O - voir mon commentaire. Je n'ai pas remarqué un upvote....
- Oui, je crois que je discutais avec vous, sur l'interprétation de l'10(base 10) 10(base 16). Je pense qu'une telle interprétation par défaut est rare, au mieux.
- Connexes: Pourquoi ne GCC utilisent la multiplication par un nombre étrange dans la mise en œuvre de division entière?: Si vous avez un rapide se multiplient, vous pouvez diviser par des constantes de compilation avec juste une multiplication et une maj de la moitié haute, obtenir le résultat correct pour chaque dividende (à la différence de la accepté de répondre).
Vous devez vous connecter pour publier un commentaire.
Voici ce que le compilateur de Microsoft n'lors de la compilation de divisions par de petites intégrale des constantes. Supposons qu'une machine 32 bits (code peut être ajustée en conséquence):
Ce qu'il se passe ici, c'est que nous sommes en multipliant par une approximation proche de 1/10 * 2^32, puis en supprimant les 2^32. Cette approche peut être adaptée à différents diviseurs et de bits différents largeurs.
Cela fonctionne très bien pour l'architecture ia32, depuis sa IMUL instruction de mettre le produit 64 bits dans edx:eax, et l'objectif de valeur sera la valeur requise. Viz (en supposant que le dividende est passé dans eax et le quotient retourné dans eax)
Même sur une machine avec une lente multiplier instruction, ce sera plus rapide que d'un logiciel de fracture.
4294967219 / 10 = 429496721
, mais4294967219 * div >> 32 = 429496722
Pour les plus grands diviseurs, la version signée seront inexactes ainsi.x/10
un point fixe inverse multiplicatif (et de faire un code supplémentaire pour gérer les négatifs des entrées pour la signature de division), à donner la bonne réponse pour tous les 32 bits d'entrées. Non signés, la division par 10, MSVC (et d'autres compilateurs) (godbolt.org/g/aAq7jx) permet de multiplier par0xcccccccd
et le déplacement à droite dans la moitié haute de 3.i/10
. Il est mauvais pour les grands nombres entiers positifs se terminant par 9, en commençant pardiv10(1073741829) = 107374183. Correct = 107374182
. C'est aussi mauvais pour la plupart (tous?) les entiers négatifs, par exemplediv10(-1) = -1. Correct = 0
. @JasonS est correct de dire que ce n'est pas de mettre en œuvre le C sémantique dex / 10
.Bien que les réponses apportées jusqu'à présent correspondre à la réelle question, ils ne correspondent pas au titre. Voici donc une solution fortement inspiré par Hacker Plaisir que vraiment utilise que peu les quarts de travail.
Je pense que c'est la meilleure solution pour les architectures qui ne disposent pas multiplier instruction.
Bien sûr, vous pouvez si vous pouvez vivre avec une perte de précision. Si vous connaissez la plage de valeur de vos valeurs d'entrée, vous pouvez venir avec un décalage de bits et d'une multiplication qui est exact.
Quelques exemples de comment vous pouvez diviser par 10, 60, ... comme il est décrit dans ce blog pour format le temps de la façon la plus rapide possible.
(ms * 205)
risque de déborder.Considérant Kuba Ober réponse, il y en a une autre dans la même veine.
Il utilise itératif rapprochement du résultat, mais je n'en attendait pas surprenant performances.
Laisser dire que nous devons trouver
x
oùx = v /10
.Nous allons utiliser l'opération inverse
v = x * 10
, car il a la propriété que lorsquex = a + b
, puisx * 10 = a * 10 + b * 10
.Laisser utiliser
x
comme variable contenant la meilleure approximation de résultat à ce jour. Lorsque la recherche se termine,x
Va contenir le résultat. Nous allons définir chaque bitb
dex
de la plus importante à la moins importante, un par un, à la fin de comparer(x + b) * 10
avecv
. Si sa plus petite ou égale àv
, alors le bitb
est situé dansx
. Pour tester le bit suivant, nous avons simplement shift b d'une position vers la droite (division par deux).Nous pouvons éviter la multiplication par 10 en tenant
x * 10
etb * 10
dans d'autres variables.Cela donne l'algorithme suivant pour diviser
v
par 10.Edit: pour obtenir l'algorithme de Kuba Ober, ce qui évite de variable
x10
, nous pouvons soustraireb10
dev
etv10
à la place. Dans ce casx10
n'est plus nécessaire. L'algorithme devientLa boucle peut être unwinded et les différentes valeurs de
b
etb10
peuvent être précalculées comme des constantes.Bien de la division est de la soustraction, donc oui. Décalage à droite d'ici le 1er (division par 2). Maintenant soustraire 5 de la suite, en comptant le nombre de fois que vous faites la soustraction jusqu'à ce que la valeur est inférieure à 5. Le résultat est le nombre de soustractions vous l'avez fait. Oh, et en divisant va probablement être plus rapide.
Une stratégie hybride de décalage à droite, puis diviser par 5 à l'aide de la répartition normale peut vous permettre une amélioration de la performance si la logique dans le diviseur n'est pas déjà le faire pour vous.
Sur les architectures qui ne peut prendre la place à un moment, une série de comparaisons explicites contre la diminution des puissances de deux, multiplié par 10 pourrait mieux fonctionner que la solution du hacker plaisir. En supposant un 16 bits dividende:
n*10
est encore bon marché:(n<<3) + (n<<1)
. Petite maj des réponses pourrait peut-être être utile sur des machines avec de lents ou inexistants HW se multiplient, et seulement un décalage de 1. Sinon un point fixe inverse est beaucoup mieux pour compiler constante de temps de diviseurs (comme les compilateurs modernes faire pourx/10
).pour développer Alois réponse un peu, nous pouvons étendre le suggère
y = (x * 205) >> 11
pour un peu plus des multiples/postes:chaque ligne est une seule, indépendante, de calcul, et vous verrez que votre premier "erreur"/résultat incorrect à la valeur indiquée dans le commentaire. vous êtes généralement mieux de prendre la plus petite maj pour une valeur d'erreur que cela permettra de minimiser la des bits supplémentaires nécessaires pour stocker de la valeur intermédiaire dans le calcul, par exemple
(x * 13) >> 7
est "meilleure" que la(x * 52) >> 9
comme il a besoin de deux moins de bits de frais généraux, tandis que les deux commencent à donner de mauvaises réponses ci-dessus 68.si vous voulez calculer plus de ceux-ci, les (Python) code peut être utilisé:
et j'ai fait la chose la plus évidente pour le calcul lorsque ce rapprochement commence à aller mal avec:
(notez que
//
est utilisé pour "entier" de la division, c'est à dire qu'il tronque/tours vers zéro)la raison de la "3/1" modèle à erreurs (c'est à dire 8 répète 3 fois suivi par 9) semble être due au changement de bases, c'est à dire
log2(10)
est ~3.32. si nous intrigue, les erreurs que nous obtenez le résultat suivant:où l'erreur relative est donnée par:
mul_from_shift(shift) /(1<<shift) - 0.1