Comment imprimez-vous un numéro IEEE754 (sans printf)?

Pour les besoins de cette question, je ne suis pas en mesure d’utiliser les installations de printf (je ne peux pas vous dire pourquoi, malheureusement, mais supposons simplement que je sache ce que je fais).

Pour un nombre à simple précision IEEE754, vous avez les bits suivants:

 SEEE EEEE EFFF FFFF FFFF FFFF FFFF FFFF 

S est le signe, E est l’exposant et F est la fraction.

L’impression du signe est relativement facile dans tous les cas, tout comme l’attrapage de tous les cas spéciaux comme NaN ( E == 0xff, F != 0 ), Inf ( E == 0xff, F == 0 ) et 0 ( E == 0, F == 0 , considéré comme spécial simplement parce que le biais de l’exposant n’est pas utilisé dans ce cas).

J’ai deux questions.

Le premier est la meilleure façon de transformer les nombres dénormalisés (où E == 0, F != 0 ) en nombres normalisés (où 1 <= E <= 0xfe )? Je pense que cela sera nécessaire pour simplifier la réponse à la question suivante (mais je peux me tromper alors n’hésitez pas à m’éduquer).

La deuxième question est de savoir comment imprimer les nombres normalisés. Je veux pouvoir les imprimer de deux manières: exponentielle comme -3.74195E3 et non exponentielle comme 3741.95 . Bien que, si on regarde ces deux côte à côte, il devrait être assez facile de transformer le premier en second en déplaçant simplement la virgule décimale. Alors concentrons-nous simplement sur la forme exponentielle.

Je me souviens vaguement d’un algorithme que j’ai utilisé il y a longtemps pour imprimer PI où vous utilisiez l’une des formules toujours réducsortingces et gardiez une limite supérieure et inférieure sur les possibilités, produisant un chiffre lorsque les deux limites étaient acceptées et décalant le calcul de un facteur de 10 (ainsi, lorsque les limites supérieure et inférieure étaient 3.2364 et 3.1234 , vous pouvez générer le 3 et l’ajuster dans le calcul).

Mais cela fait longtemps que je n’ai pas fait cela, alors je ne sais même pas si c’est une approche appropriée à adopter ici. Il semble en être ainsi puisque la valeur de chaque bit est la moitié de celle du bit précédent lors du déplacement dans la partie fractionnaire ( 1/2 , 1/4 , 1/8 , etc.).

Je préférerais vraiment ne pas avoir à parcourir le code source de printf moins que cela ne soit absolument nécessaire, alors, si quelqu’un peut m’aider, je vous en serai éternellement reconnaissant.

Si vous voulez obtenir des résultats exacts pour chaque conversion, vous devrez utiliser une arithmétique à précision arbitraire, comme dans les implémentations de printf (). Si vous voulez obtenir des résultats «proches», ne différant peut-être que par le ou les chiffres les moins significatifs, un algorithme très simple basé sur la double précision suffit: pour la partie entière, divisez de manière répétée par dix et ajoutez les rests à forme la chaîne décimale (en sens inverse); pour la partie décimale, multipliez à plusieurs resockets par dix et soustrayez les parties entières pour former la chaîne décimale.

J’ai récemment écrit un article sur cette méthode: http://www.exploringbinary.com/quick-and-dirty-floating-point-to-decimal-conversion/ . Il n’imprime pas de notation scientifique, mais cela devrait être sortingvial à append. L’algorithme imprime des nombres inférieurs à la normale (ceux que j’ai imprimés sont sortis avec précision, mais il faudrait effectuer des tests plus approfondis).

Les nombres dénormalisés ne peuvent pas être convertis en nombres normalisés du même type à virgule flottante. L’exposant du nombre normalisé équivalent sera trop petit pour être représenté par l’exposant.

Pour imprimer des nombres normalisés, une façon idiote à laquelle je peux penser est de multiplier de manière répétée par 10 (enfin, pour la partie décimale).

La première chose à faire est de convertir l’exposant en décimal (car c’est vraisemblablement ce que vous voulez voir dans la sortie) à l’aide de logarithmes. Vous prenez la fraction de ce résultat et multipliez la mantisse par l’exp10 de cette fraction, puis vous la convertissez en caractères décimaux. A partir de là, il vous suffit d’insérer le point décimal à l’emplacement approprié, décalé de l’exposant maintenant décimal.

Un article de G. Steele décrit plus en détail un algorithme qui semble reposer sur le même principe que celui que vous décrivez. Si la mémoire est en service, il est un temps où vous êtes obligé d’utiliser une arithmétique de précision sans limite. (Je pense que c’est Comment imprimer des nombres à virgule flottante avec précision, mais que la mémoire est en baisse, je ne peux pas confirmer et les résultats de Google sont pollués par un document rétrospectif identique 20 ans plus tard).