jeudi 10 septembre 2020

Chapitre 90 : assembleur ARM 32 bits : affichage des nombres en virgule flottante : algorithme grisu et grisu 2

 

Suite à l’écriture de la routine d’affichage pour l’assembleur 64 bits, je me suis penché à nouveau sur la routine pour le 32 bits que j’avais écrit au chapitre 11 au tout début de ma découverte de l’assembleur ARM. Bon c’est pas très joli joli : la routine est fausse et les résultats ne sont pas très bons. Je suis donc repartit du document pdf de Florian Loitsch (voir : https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf ) et de la transposition en 64 bits que j’ai faite au chapitre 88 pour essayer de réécrire ces algorithmes pour le 32 bits.

Dans le programme convertFloat32.s, j’ai donc transposé l’algorithme grisu. Cela pose quelques difficultés car les nombres en virgule flottante traités sont en double précision et donc nécessitent une zone de 64 bits pour stocker la mantisse et les opérations doivent donc s’effectuer avec 2 registres . L’exposant lui peut être stocké sur 32 bits. De plus en 32 bits, le nombre de registres est limité (13) et donc il faut jongler avec pour arriver à tout calculer.

Dans ce premier programme, je reprends la trame de l’algorithme : conversion du nombre contenu dans le registre d0, normalisation, calcul du coefficient K pour accéder à la table des puissances de 10.

A ce propos, en découvrant sur github, cette méthode écrite pour d’autres langages (C,c++, java), j’ai constaté une différence entre les valeurs de ma table et celles publiées pour les mêmes puissance de 10. Je pense que mes valeurs sont calculées avec une moindre précision que les valeurs publiées.

La multiplication de 2 nombres codés suivant la structure proposée m’a posé quelques difficultés surtout dans le report des retenues.

J’ai gardé le découpage du résultat en 3 zones comme proposé mais l’affichage n’est pas satisfaisant : en utilisant la routine de conversion habituelle y a affichage de caractères blancs avant les chiffres du résultat.

Le résultat affiché est juste jusqu’à 17 chiffres et les valeurs suivantes sont fausses.

Dans le deuxième programme convertFloat32G2V2.s, je programme l’algorithme grisu 2 avec une table réduite par pas de 8 et une table reconstituée à partir d’une trouvée sur Internet.

Les principales difficultés sont de bien gérer les déplacements gauche et droite de bits sur 2 registres 32 bits. Dans la sous routine genererChiffres, pour pouvoir effectuer les calculs sans avoir à sauvegarder des valeurs en mémoire, je stocke certains registres dans les registres s0,s1,s2,et s3 . On gagne pas grand-chose car pour une routine utilisable par d’autres programmes et pour éviter tout problème, il faut sauvegarder quand même ces registres en début de routine et les restaurer en fin.

Pour le formatage, du résultat final, j’ai fait assez simple soit l’exposant est à zéro et j’affiche tous les chiffres disponibles et si l’exposant est différent de zéro, j’affiche le résultat sous la forme nombreEexposant. Il est donc possible d’améliorer cet affichage pour produire d’autres formats.

La routine donne des résultats satisfaisant avec toujours au maximum 17 chiffres significatifs. Reste à savoir si elle est toujours exacte !!!!





mardi 1 septembre 2020

Chapitre 89 : Assembleur ARM 64 : routine calcul inverse racine carrée rapide pour des nombres en virgule flottante

 

En recherchant des algorithmes pour le chapitre précédent, je suis tombé sur une routine pour calculer la racine carrée inverse d’un nombre.Elle va nous permettre de tester la conversion de la routine grisu2 précédente en module.

Pour cela j’ai supprimé tout ce qui était inutile dans le programme grisu2NV2_64.s et je l’ai renommé routConvFloat64.s. J’ai ensuite compilé ce programme pour créer un objet.

Ensuite dans le programme calculinvRac, j’ai écrit la routine de calcul telle qu’elle est exposée dans Wikipedia et je l’ai compilé puis linké avec l’objet précédent.

Voici le résultat pour le nombre donnée en exemple dans Wikipedia :

2.529810967007374 (la valeur exacte est 2,52982)

et pour 4 :

0.4999978544248724 (la valeur exacte est 0,5

Il n’est donc plus nécessaire de faire appel à l’instruction printf du C pour afficher les nombres en virgule flottante.

C’est tout pour aujourd’hui !!!



Chapitre 88 : Assembleur ARM 64 bits : affichage des nombres en virgule flottante : algorithme grisu

 

Dans plusieurs chapitres précédents, lors de manipulations de nombres en virgule flottante, j’ai du faire appel à l’instruction printf du langage C pour afficher les résultats.

Cet été j’ai recherché sur internet des algorithmes pour effectuer la conversion directement en langage assembleur mais uniquement en 64 bits. J’ai commencé par trouver un algorithme dragon4 (voir ) mais son adaptation me semblait complexe. Mais ce site aussi renvoyait sur un document pdf (https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf ) qui proposait 3 algorithmes pour convertir les floats.

J’ai donc épluché ce document et commencé par écrire un premier programme pour étudier en détail le premier algorithme proposé : grisu.

Cet algorithme fait appel à une table des puissances de 10 converties en une structure utilisable par l’algorithme. Donc j’ai du écrire un premier programme (genTableDeTableORI64.s) qui convertit toutes les puissances de 10 entre 10E-309 et 10E307 dans le format décrit dans le document ci dessus.

Curieusement, bien que la norme IEEE754 suppose des puissances de 10 au delà de cette plage, le compilateur as refuse les floats avec des puissances inférieure à -309 et supérieure à +307. Ici, j’ai fait un programme simple qui affiche les résultats que j’ai ensuite recopiés dans un fichier à insérer dans le programme grisu64.s.

Dans ce programme, j’ai repris les idées développées par Florian Loitsch : structure des données, calcul du coefficient pour accéder à la puissance 10 de la table, multiplication, et éclatement du résultats en 3 zones pour l’affichage.

Cela m’a permis d’éclaircir certains points, et d’effectuer les tests pour arriver à un résultat satisfaisant. Bon d’accord l’affichage est plutôt brut mais les résultats trouvés sont corrects (mais avec une perte de précision).

Exemple :

Affichage de 0f12345E-10 :

123450000000000005536E-26

Affichage de Pi : 0f314159265358979323E-17 :

31415926535897931160E-19

Affichage de -0.3 :

-29999999999999993976E-20

Pour ce programme, la table des puissance de 10 contient 309 + 307 = 616 postes, ce qui est beaucoup. Mais le document en référence propose un deuxième algorithme grisu2 qui utilise une table avec des puissance de 10 avec un pas de 8, ce qui réduit le nombre de poste à 78 postes.

D’autres part, en 64 bits la multiplication proposée peut être réduite à une seule instruction umulh. Puis l’analyse de l’algorithme fait apparaître qu’il n’ait nécessaire que d’avoir 4 valeurs avec la structure proposée. Donc il est possible de n’utiliser que des registres 64 bits pour gérer ces valeurs sans avoir à les stocker en mémoire.

Le programme grisu2NV2_64.s met en pratique ces idées. De l’entrée de la valeur à convertir contenue dans le registre d0 à l’extraction des chiffres du résultat, il n’y a aucun appel à une sous routine !! tous les calculs sont effectués dans la même routine. Ensuite les affichages en nombre entiers, nombres décimaux et nombre en notation scientifique (xEexposant) sont effectués par une seule sous-routine. Cela allège le programme mais complique la lisibilité !!

Bien sûr la table des puissances de 10 utilisée est différente et elle est plus petite.

Les résultats sont corrects mais je ne garantie pas leur exactitude pour tous les cas. 

Pi :

3.141592653589793
 

affichage décimal :

-0.3

 

Notation scientifique

-1.234e+306

 

Ce programme doit pouvoir être amélioré (par exemple en améliorant l’affichage de certains nombres comme 1E23 ou 1E53 )et encore optimisé !!