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 16-bits Assembleur Discussion :

Comment afficher un nombre à virgule ?


Sujet :

x86 16-bits Assembleur

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 19
    Points : 8
    Points
    8
    Par défaut Comment afficher un nombre à virgule ?
    Bonsoir,

    Je préfère prévenir d'avance afin que personne ne perde son temps : les gens qui veulent bien m'aider vont devoir faire un gros effort de compréhension car je reconnais que j'ai du mal à expliquer clairement mon problème (en même temps, c'est toujours un peu le cas quand on est perdu).

    Prenons un nombre, disons 10,5. Écrivons ce nombre en hexadécimal ... Pour la partie entière : ça va de soi. Pour la partie fractionnaire : multiplication successive par 16 jusqu'à obtenir la précision voulue ou une partie fractionnaire nulle. Bref, ça donne : A,8 . Jusque là ...

    Soit un programme ASM 16 bits (émulation DOS) qui réalise une division dont le résultat est 10,5. Ce résultat doit être affiché en hexa. Suite à cette division, AX contient 10 et DX contient 5. Pour afficher la partie entière, il suffit d'avoir un tableau en mémoire qui, à chaque index du tableau, associe le caractère affichable correspondant (0 -> '0', 1 -> '1'). De cette manière on appelle la fonction d'affichage d'un caractère (ah = 02) de cette façon MOV DL, [TAB_Hexa + AX] et notre nombre apparait à l'écran. Jusque là ok.

    C'est pour la partie fractionnaire que cela se complique. On a le nombre 5 dans notre registre DX ou plus précisément sa représentation binaire : 101.
    Comment faire pour afficher ce nombre ?
    • On ne peut pas utiliser la méthode du tableau énoncé ci dessous car on a alors un 5 qui s'affiche et ce n'est pas la bonne réponse,
    • On ne peut pas multiplier chaque bit par son coefficient (ex. : 1*2^-1 + 0*2^-2 + 1*2^-3) car on obtient alors 0.625 en base 10 au lieu de 0.5,
    • On ne peut pas non plus rajouter un 0 à la fin et grouper les chiffres par 4 afin de trouver l'équivalent hexa car dans ce cas, on trouve A (1010 en binaire).

    Comment faire alors pour afficher la partie fractionnaire en hexa ou en décimal ? Ais-je loupé quelque chose ?

    Je suppose que c'est pour répondre à ce genre de problématiques (en plus du fait que tous les nombres fractionnaires ne peuvent pas être représenté en virgule fixe) qu'ont été créées les FPU et l'émulation logicielle de la irgule flottante ...

    Merci d'avance.

  2. #2
    Membre éclairé
    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
    Points : 701
    Points
    701
    Billets dans le blog
    1
    Par défaut
    pour afficher un chiffre à l'ecran, faut passer par une table de caractère, comme par exemple l'ascii.

    donc, en ascii, le chiffre 0 est egal à 30h (je crois), c'est à dire '0'
    et le chiffre 5 est egal à 35h

    donc, pour afficher le chiffre 5, il faut juste envoyer à l'ecran texte, ou à la fonciton printf, l'octet 35h, c'est à dire le caratère '5'
    c'est tout bête.

    pour ce qui est de l'affichage de la virgule, bein, suffit de savoir si on est en fixe ou en flottant.
    en fixe, c'est facile, la virgule est definie par un simple indice de chiffre.
    par exemple pour dire, 4 chiffres après la virgule, ça signifie qu'il faut envoyer le caractère virgule ',' au bout de 4 iterations de notre division par 10.
    pour la virgule flottante, il faut extraire l'exposant de sorte à connaitre l'emplacement de cette virgule, recuperer la mantisse, et convertir cette mantisse comme pour n'importe quel nombre. la virgule sera positionnée au moment voulu, indiqué par l'exposant.

  3. #3
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 19
    Points : 8
    Points
    8
    Par défaut
    Merci de votre réponse.

    Je sais afficher un caractère à l'écran, je l'ai dit : la partie entière ne me pose aucun problème, par exemple.

    Je ne peux pas envoyer le caractère '5' à l'écran car ce n'est pas la bonne réponse. Je veux afficher 10,5 (le résultat de ma division) en hexadécimal.
    Donc la réponse est A,8 (car A*16^0 = 10 et 8*16^-1 = 0.5 donc 10,5 en base 10).

    Pour la partie entière, pas de problème, je fais ça avec un tableau ... ce qui revient au même que faire add registre, 30h comme vous le suggérez.

    Pour la partie fractionnaire, là je galère. Il me faut afficher le caractère '8'. Mais moi je n'ai pas 8 dans mon registre mais le chiffre 5 donc comment faire la conversion. Là est réellement ma question.

  4. #4
    Rédacteur
    Avatar de Neitsa
    Homme Profil pro
    Chercheur sécurité informatique
    Inscrit en
    Octobre 2003
    Messages
    1 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Chercheur sécurité informatique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 1 041
    Points : 1 956
    Points
    1 956
    Par défaut
    Bonjour,

    en fait ça fonctionne comme les divisions en base 10, sauf qu'on est en base 16 (ok, je sais c'est crétin mais l'explication suit). Démo:

    [Warning: Tous les calculs sont en base 16 !!!]

    1) 15 / 2 = A reste 1
    2) Je pose ma virgule, je descend un 0
    3) 10 / 2 = 8 reste 0

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    15  |_2
     10 | A.8
      0 |
    Autre exemple:

    1) 10 / 7 = 2 reste 2
    2) Je pose ma virgule, je descend un 0
    3) 20 / 7 = 4 reste 4
    4) je descend un 0
    5) 40 / 7 = 9 reste 1
    6) descend un 0
    7) 10 / 7 = 2 reste 2
    8) descend un 0
    9) 20 / 7 = 4 reste 4 [ok nombre cyclique...]

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    10     |_ 7
     20    | 2.49249...
      40   |
       10
        20
    Concrètement, la "descente du zéro" en base 16 correspond à une multiplication du reste de la division par 16 en base 10.

  5. #5
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 19
    Points : 8
    Points
    8
    Par défaut
    Merci de votre réponse.

    Mais une fois encore, sur le papier je sais changer des nombres de base ... C'est lorsque je passe à mon code assembleur que je me plante.

    Pour résumer afin que tout le monde comprenne mieux :
    1) Mon programme ASM effectue une division, on s'en fout de ce qui est divisé, ce n'est pas le sujet. Tout ce qu'on sait, c'est que ce résultat s'exprime 10,5 en base 10.

    2) Le résultat de la division ne tombe pas sur un entier. Comme je n'utilise pas le FPU, il y a donc le quotient entier dans un registre et le reste dans un autre. (AX = 10, DX = 5 pour être précis).

    3) Je veux afficher le quotient et le reste en héxa (donc A,8h [équivalent de 10,5d]).

    4) Pour la partie entière pas de problème, je fais la méthode exposée dans mes précédents billets et j'affiche un A.

    5) Pour la partie fractionnaire, je ne sais pas comment faire. Si je fais la même méthode, j'affiche un 5 ... Or A,5h != 10.5d ...

    J’espère avoir réussi à exprimer mon problème.

  6. #6
    Rédacteur
    Avatar de Neitsa
    Homme Profil pro
    Chercheur sécurité informatique
    Inscrit en
    Octobre 2003
    Messages
    1 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Chercheur sécurité informatique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 1 041
    Points : 1 956
    Points
    1 956
    Par défaut
    Hello,

    Citation Envoyé par DR0GV3 Voir le message
    Merci de votre réponse.

    Mais une fois encore, sur le papier je sais changer des nombres de base ... C'est lorsque je passe à mon code assembleur que je me plante.

    2) Le résultat de la division ne tombe pas sur un entier. Comme je n'utilise pas le FPU, il y a donc le quotient entier dans un registre et le reste dans un autre. (AX = 10, DX = 5 pour être précis).
    J'ai bien compris ton problème et l’explication que je donnais au dessus reste valide. Si tu sais le faire sur papier alors c'est la même chose algorithmiquement.

    C'est ton deuxième point qui est erroné, du coup tu buttes sur un problème qui n'existe pas:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    ; 32-bit mais l'idée est la même en 16-bit
    ; divise EDX:EAX par ECX. Le quotient est dans EAX, le reste dans EDX
     
    mov eax, 15 ; EAX = 21 (dividende)
    xor edx, edx
    mov ecx, 2   ; ecx = 2 (diviseur)
    div ecx
     
    ; EAX = A ; EDX = 1
    Il reste 1 et non pas 5 ! A partir de là, tu multiplies le reste par 16 (SHL EDX, 4) et tu divises encore par le même diviseur (ECX) tu tu obtiens bien 8 de quotient. Bingo !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    ; 32-bit mais l'idée est la même en 16-bit
    ; divise EDX:EAX par ECX. Le quotient est dans EAX, le reste dans EDX
     
    mov eax, 15 ; EAX = 21 (dividende)
    xor edx, edx
    mov ecx, 2   ; ecx = 2 (diviseur)
    div ecx
    ; EAX = A ; EDX = 1 ; ==> print EAX ; reste != 0 => continue
    mov eax, edx ; EAX = reste de la division
    xor edx, edx
    shl eax, 4 ; reste * 16
    div ecx
    ; EAX = 8 ; EDX = 0 ==> print EAX ; reste = 0 fin de la division

  7. #7
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Cela dépend beaucoup de la provenance de ton programme assembleur. Si c'est toi qui l'a fait, tu auras plus vite fait d'en écrire un autre directement plutôt qu'essayer d'adapter le résultat.

    Le problème vient du fait que, comme tu l'expliques, tu obtiens non pas un nombre rationnel mais un quotient et un nombre entier censé représenter ce qu'il y a après la virgule. Ce n'est donc pas la « vraie » valeur, puisque 0,5 en décimal s'écrit 0,1 en binaire, et donc que 10,5d donne 1010,1b.

    Il faut donc d'abord convertir « 5 » vers « 0,5 », en le divisant par 10, donc, puis extraire chaque chiffre de cette valeur en base 16, en faisant des multiplications par 16. « 0,1b × 10000b = 1000b », donc 8, ce qui est bien ce que tu cherches.

    Le mieux est donc de multiplier d'abord par 16 puis diviser par 10 pour toujours rester sur un nombre entier. Cela soulève quand même deux problèmes :

    • Il faut connaître à l'avance l'ordre de grandeur du nombre (pour savoir par quelle puissance de dix le diviser) ;
    • Il faut faire encore une division. Certes, ce n'est qu'une division entière, mais c'est censé être déjà l'objet de ton programme assembleur. Soit tu le rappelles, soit tu le modifies de façon à ce qu'il utilise directement les bonnes bases.

  8. #8
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 19
    Points : 8
    Points
    8
    Par défaut
    Merci à vous deux pour votre aide votre patience.

    Obsidian :
    Cela dépend beaucoup de la provenance de ton programme assembleur. Si c'est toi qui l'a fait, tu auras plus vite fait d'en écrire un autre directement plutôt qu'essayer d'adapter le résultat.
    C'est un programme personnel parmi d'autres afin d’expérimenter, comprendre les choses (ou du moins essayer de les comprendre ...). Je me suis donné une problématique et je tente de la résoudre. Rien de plus.

    Il faut connaître à l'avance l'ordre de grandeur du nombre (pour savoir par quelle puissance de dix le diviser) ;
    En effet, avec mon diviseur toujours égal à 10, je suppose que me suis mis dans une situation particulière dans laquelle le reste va de 0 à 9 et est 10 fois trop grand par rapport à la réalité.

    C'est pour cela que l'on a créé les coprocesseurs et les bibliothèques logicielles permettant de faire des calculs en virgule flottante, je suppose ? Pour éviter à avoir à s’occuper de ce genre de chose en plus d'améliorer la précision et l'ensemble des nombres représentables ?

    Il faut faire encore une division. Certes, ce n'est qu'une division entière, mais c'est censé être déjà l'objet de ton programme assembleur. Soit tu le rappelles, soit tu le modifies de façon à ce qu'il utilise directement les bonnes bases.
    Oui mais la division effectuée par mon programme n'a rien à voir avec un changement de base et tout ça. Pour l'instant, il s'agit d'une division entre 2 nombres, dont le diviseur est forcé à 10d.


    Neitsa :
    Je dois être bête, ce n’est pas possible autrement ! J'ai réussi à m'embrouiller moi-même ... J'ai passé une bonne partie de la fin d’après-midi à tenter de démêler tout ça.

    Je me demande maintenant : est-ce le même procédé pour toutes les bases ? Je m'explique :
    Base 16 : Je prends le reste, je le multiplie par 16 et je divise tout ça par le même diviseur que pour la partie entière

    Base 8 : Je prends le reste, je le multiplie par 8 et je divise tout ça par le même diviseur que pour la partie entière ?

    Base 2 : Je prends le reste, je le multiplie par 2 et je divise ça par le même diviseur que la partie entière ?

    Je pense que oui puisqu'on cherche toujours à faire une opération du genre :
    1) Récupérer le reste, le diviser par 10 car il est 10 fois trop grand
    2) Multiplier ce nouveau reste par la base (2,8,16, ...)
    3) Garder la partie entière
    4) Faire 3) et 4) tant que le reste != 0 ou que l'on n'a atteint la précision voulue

  9. #9
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 369
    Points : 23 623
    Points
    23 623
    Par défaut
    Citation Envoyé par DR0GV3 Voir le message
    C'est pour cela que l'on a créé les coprocesseurs et les bibliothèques logicielles permettant de faire des calculs en virgule flottante, je suppose ? Pour éviter à avoir à s’occuper de ce genre de chose en plus d'améliorer la précision et l'ensemble des nombres représentables ?
    Comme je te le disais plus haut, cela ne fait que transposer le problème : si tu crées une bibliothèque logicielle, tu déplaces le morceau de programme que tu as écrit vers une bibliothèque tierce. Donc, aucun changement au niveau du code. Si tu fabriques un co-processeur mathématique, il faut le concevoir de la même façon que tu as conçu le logiciel.

    La raison pour laquelle on les a produits, donc, est la même que pour tout le reste dans l'industrie. C'est pour avoir à éviter de réécrire sans arrêt la même chose, d'une part, et pour gagner en rapidité d'exécution, d'autre part, quand ces calculs sont fréquents.

    Oui mais la division effectuée par mon programme n'a rien à voir avec un changement de base et tout ça. Pour l'instant, il s'agit d'une division entre 2 nombres, dont le diviseur est forcé à 10d.
    Les changements de base se font à l'aide de divisions.


    Je me demande maintenant : est-ce le même procédé pour toutes les bases ? Je m'explique :[…]
    Oui, tout-à-fait ! Les différentes bases ne sont qu'un moyen de représenter un même nombre. Dans le même esprit, tu pourrais tout aussi bien choisir de les écrire en chiffres romains, par exemple.

    L'avantage de l'hexadécimal (et c'est pour cela qu'on l'utilise) est que « 16 » est une puissance de 2. Donc, tu peux prendre un raccourci en allant lire les bits directement. C'est exactement comme convertir de la base 10 vers la base 100 : on s'aperçoit qu'il suffit de lire les chiffres deux par deux pour former le nouveau nombre. Et c'est vrai pour toutes les bases puissance d'une autre.

    Maintenant, dans le cas qui t'intéresse, tu te retrouves bloqué parce que tu obtiens « 5 » dans ton registre alors que tu voudrais traiter en fait « 0,5 ». En décimal, il s'agit de la même valeur décalée d'une colonne mais pas en binaire ni dans les bases qui en dérivent. En binaire, 5d ÷ 10d donne :

    « 101b ÷ 1010b = 0,1b ».

    Même en faisant abstraction de ce qu'il y a à gauche de la virgule, on voit bien que « 101 » et « 1 » ne sont pas les mêmes valeurs. Puisque tu passes de la base 10 à la base 16 qui ne sont pas directement compatibles entre elles, tu es obligé de faire une division ordinaire pour obtenir ton résultat.

  10. #10
    Futur Membre du Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    19
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 19
    Points : 8
    Points
    8
    Par défaut
    Obsidian :
    Encore une fois : merci pour ta réponse.

    Citation Envoyé par Obsidian Voir le message
    Les changements de base se font à l'aide de divisions.
    Je l'ai bien compris mais je répondais à une de tes phrases dans laquelle tu disais qu'il fallait que je repense intégralement mon programme car il n'était pas normal qu’il y ait une division de plus pour effectuer la conversion vu que c'était le but de mon programme de faire des divisons. Donc, je t'ai répondu que le fait qu'il y ait deux divisions dans mon programme ne m’apparait pas être un problème de conception. Une division pour effectuer le premier travail (diviser deux nombres donnés entre eux) et une autre pour effectuer la conversion voulue.

    Je n'ai rien à ajouter concernant le reste de ton message. J'ai bien compris mon erreur : je savais que, pour afficher mon reste en hexa ou en binaire ou en octal, il faillait que je multiplie le reste par la base de destination. Mais je bloquais sur le fait que le registre DX contienne le reste entier de ma division et non pas "0,5". Donc je ne voyais pas comment passer de 5 à 0.5 ... et je cherchais vainement une méthode pour diviser par 10 sans même penser que je pouvais faire l'inverse : multiplier par la base et diviser par 10 ensuite. Voilà.

    Mon problème étant résolu, je passe ce sujet en résolu, merci aux trois personnes qui se sont penchées sur mon problème .

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 8
    Dernier message: 18/04/2011, 14h46
  2. Réponses: 18
    Dernier message: 06/10/2007, 18h03
  3. Réponses: 5
    Dernier message: 20/04/2007, 09h00
  4. Comment afficher le nombre de connectés?
    Par baleiney dans le forum Langage
    Réponses: 2
    Dernier message: 22/10/2006, 00h11
  5. Réponses: 8
    Dernier message: 06/04/2006, 09h01

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