IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

x86 32-bits / 64-bits Assembleur Discussion :

Instructions division, div-idiv


Sujet :

x86 32-bits / 64-bits Assembleur

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé Avatar de Array
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    210
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 210
    Par défaut Instructions division, div-idiv
    Bonjour,

    Voilà je me pose quelques questions au sujet de div et idiv.
    Les instruction de division en x86, comme vous le savez, partent avec un dividende d'une taille x, un diviseur d'une taille x/2, et donnent un quotient de taille x/2.

    Supposons que EDX:EAX contienne le plus gros nombre possible qu'ils peuvent contenir, soit ffffffffffffffff (soit 64 bits mis à 1), et que ECX contienne 2.

    Si je fais
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    mov eax, ffffffffffh;
    mov edx, ffffffffffh;
    mov ecx, 2;
    div ecx;
    Le résultat auquel on s'attend serait 7fffffffffffffff. Or, on sait très bien que la division échouera, puisque EAX ne peut contenir qu'un nombre de 32 bits maximum, et que c'est justement à 32bits qu'est consigné le quotient.

    Ma question est: Y a-t-il un moyen de circonvenir le problème?
    Dans le cas d'une opérande 32 bits divisé par une opérande 16 bits, par example ffffffff divisé par 2, c'est facile: On a qu'à étendre ffffffff à EAX:EDX, et d'étendre le diviseur dans une opérande 32 bits, puis on fait la division et on obtient le bon quotient, puisque ce dernier se trouve consigné dans une opérande 32 bits.


    Par contre, je ne vois pas de solution pour diviser un 64 bits par un 2 étendu dans une opérande 32 bits.
    Or, en C, c'est parfaitement possible.

    Prenons le code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    #include <stdio.h>
     
    int main(void) {
    	unsigned long long int a = 0xffffffffffffffff, b;
    	b = a/2;
    	printf ("%ullx\n", b); // %I64x sur Windows
    	return 0;
    }
    Le résultat affiché par printf() sera celui attendu, soit 7fffffffffffffff:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    I:\>a.exe
    7fffffffffffffff
    Bref, comment est-ce que le compilateur en C fait pour que le bon résultat puisse être obtenu?

    Merci bien,
    Cordialement,
    Array

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 499
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 499
    Par défaut
    Citation Envoyé par Array Voir le message
    Le résultat auquel on s'attend serait 7fffffffffffffff. Or, on sait très bien que la division échouera, puisque EAX ne peut contenir qu'un nombre de 32 bits maximum, et que c'est justement à 32bits qu'est consigné le quotient.
    Effectivement, le micro-processeur déclenchera une exception, dans ce cas.

    Ma question est: Y a-t-il un moyen de circonvenir le problème?

    Par contre, je ne vois pas de solution pour diviser un 64 bits par un 2 étendu dans une opérande 32 bits. Or, en C, c'est parfaitement possible. Bref, comment est-ce que le compilateur en C fait pour que le bon résultat puisse être obtenu?
    On procède comme à l'école, sur papier : on va procéder avec des mots deux fois plus courts (donc 16 bits sur une architecture 32) en commençant par le plus significatif (donc le plus à gauche), en posant le quotient, et en « faisant descendre » la tranche suivante, ce qui va consister, dans notre cas, à faire passer le reste d'un registre à l'autre (pour le « décaler à gauche » en réalité) et à charger la nouvelle tranche dans le registre qui était dédié au reste.

    De cette façon, tu peux diviser des nombres de n'importe quelle longueur, ce qui est très utile dans le traitement des CRC, par exemple.

  3. #3
    Membre confirmé Avatar de Array
    Profil pro
    Inscrit en
    Juillet 2007
    Messages
    210
    Détails du profil
    Informations personnelles :
    Âge : 34
    Localisation : France

    Informations forums :
    Inscription : Juillet 2007
    Messages : 210
    Par défaut
    Y a-t-il une instruction spécifique pour effectuer le travail, où doit on tout coder manuellement?

  4. #4
    Membre expérimenté

    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    193
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 193
    Par défaut
    On peut aussi utiliser le FPU,ce qui est LA solution.

  5. #5
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 499
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 499
    Par défaut
    Citation Envoyé par ToutEnMasm Voir le message
    On peut aussi utiliser le FPU,ce qui est LA solution.
    Tous les CPU ne sont pas équipés d'un FPU, loin s'en faut. À vrai dire, rares sont les micro-processeurs qui en sont équipés. Même sur PC, il a fallu attendre le 486DX pour qu'ils en soient équipés par défaut.

    Par ailleurs, savoir faire des calculs élémentaires est exigible de tout programmeur, spécialement s'il développe en assembleur. Enfin, dans le cas d'absence d'un copro, on utilise tout naturellement une routine pour faire ces calculs… routine qui a bien dû être conçue et écrite par un programmeur.

    Sinon, cela veut dire que les informaticiens délèguent leur travail aux électroniciens et aux fondeurs de puce.

  6. #6
    Membre très actif
    Avatar de edfed
    Profil pro
    être humain
    Inscrit en
    Décembre 2007
    Messages
    476
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : être humain

    Informations forums :
    Inscription : Décembre 2007
    Messages : 476
    Billets dans le blog
    1
    Par défaut
    c'est sur que du point du vue du puriste de l'asm qui aime le risc, il ne faut pas utiliser fpu et autres extension.
    par contre, pour les puristes du x86, qui aiment bien le cisc et les new technologies, ça le fait, utiliser la fpu ou pire, les extensions vectorielles SIMD et autres, ça peut etre un sacré plus.

    mais il faut la solution risc, à base de code purement 32 bits ou 16 bits.

    la solution proposée par obsidian est pas mal, mais je rappele qu'en contexte d'utilisation de div et idiv, le registre (e)dx est supposé etre l'extension de signe (ou de zero) du registre (e)ax, et donc, aucune valeur d'entrée ne peut depasser les 32 bits en 32 bits, ou 16 bits en 16 bits.
    ce qui n'empeche pas evidement d'avoir un registre edx avec des chiffres significatifs avant division... mais bon, c'est pas conseillé de bosser trop près des limites sur une machine comme les PC, vu la quantité de données qu'on peut être amené à traiter, on ne peut se permettre d'avoir des sources d'erreurs potentielles.

  7. #7
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 499
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 499
    Par défaut
    Citation Envoyé par Array Voir le message
    Je vois, quelque chose comme (x + y)/2 = x/2 +y/2.
    Sauf que s'il y a un "remainder" pour x, et un autre pour y, que se passe-t-il? Y a-t-il une instruction spécifique pour effectuer le travail, où doit on tout coder manuellement? Merci!
    L'idée est de faire une boucle. L'algorithme est le même que celui que tu utilises à l'école. Tu as un seul reste à chaque tour de boucle, et celui-ci est directement impliqué dans l'opération suivante, sans avoir à faire d'addition explicite. Comme ce reste est, par définition, inférieur à ton diviseur, il prendra sa place dans la partie haute de ton dividende, certes, mais le résultat tiendra forcément dans l'équivalent de la partie basse, donc dans le registre qui lui est affecté.

    Imaginons que tu veuilles diviser 7FFF.FFFF.FFFF.FFFF (donc 64 bits) par 5 sur une machine 32 bits :

    • Tu prends le premier mot de 32 bits à gauche et tu le places dans EAX (à droite), en mettant EDX (à gauche) à zéro. Donc EDX=00000000 et EAX=7FFFFFFF ;
    • Tu divises par 5. EAX contient le quotient en totalité et EDX le reste, soit respectivement 19999999h et 00000002h ;
    • Tu poses EAX (en mémoire, donc, soit dans la pile, soit avec un registre d'index tel que DI), et tu passes le reste dans EAX pour en fait le multiplier par la taille du mot, et tu descends la suite (donc FFFF.FFFF) dans EAX. Tu redivises le tout par 5 : 0000.0002.FFFF.FFFF ÷ 5 ;
    • Le résultat tient forcément dans 32 bits : 00000000.99999999. Donc EAX=99999999h et EDX=2. Tu poses EAX comme à l'étape précédente.


    À ce stade, tu as fini ta division car tu as épuisé tous les mots, mais tu as effectivement un reste : c'est normal car 7FFFFFFFFFFFFFFF n'est pas divisible par cinq.

    Reste à vérifier si le résultat est correct : 1999999999999999 × 5 = 7FFFFFFFFFFFFFFD ; + 2 (reste) = 7FFFFFFFFFFFFFFF. Ça colle.

Discussions similaires

  1. [MySQL] Enregistrements d'une table dans des divisions <div>
    Par mouadmagan dans le forum PHP & Base de données
    Réponses: 17
    Dernier message: 05/04/2011, 18h06
  2. Division par zéro (#DIV/0) dans Excel
    Par MCoder dans le forum Langage
    Réponses: 5
    Dernier message: 12/10/2008, 19h27
  3. Diviser un div en deux avec du CSS ?
    Par beegees dans le forum Mise en page CSS
    Réponses: 1
    Dernier message: 25/09/2008, 14h26
  4. instruction pour le division
    Par omlov88 dans le forum C#
    Réponses: 2
    Dernier message: 16/11/2007, 15h16
  5. L'instruction DIV
    Par Spacy_green dans le forum x86 16-bits
    Réponses: 10
    Dernier message: 13/02/2006, 05h57

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo