web-dev-qa-db-fra.com

Quel est cet algorithme pour convertir des chaînes en chiffres appelés?

J'ai récemment fait du travail à Parsec et pour mon langage de jouet, je voulais être exprimable des numéros de fractionnels multi-basés. Après avoir creusé un peu à la source de Parsec, j'ai trouvé leur mise en œuvre d'un analyseur de numéro de point flottant et la copie pour apporter les modifications nécessaires.

Je comprends donc ce que ce code fait et vaguement pourquoi (je n'ai pas encore élaboré les mathématiques encore, mais je pense que je reçois le gist). Mais d'où vient-il? Cela semble être un moyen assez intelligent de transformer des cordes en flotteurs et en Ints, existe-t-il un nom pour cet algorithme? Ou est-ce juste quelque chose de base qui est un trou à ma connaissance? Les gens derrière Parsec le conçoivent-ils?

Voici le code, d'abord pour les entiers:

number' :: Integer -> Parser Integer                                        
number' base = 
    do { digits <- many1 ( oneOf ( sigilRange base ))
       ; let n = foldl (\x d -> base * x + toInteger (convertDigit base d)) 0 digits
       ; seq n (return n)
       }

Donc, l'idée de base ici est que digits contient la chaîne représentant la partie de numéro entière, c'est-à-dire "192". Le foldl convertit chaque chiffre individuellement en un nombre, puis ajoute que le total exécuté multiplié par la base, ce qui signifie que, à la fin de chaque chiffre, a été multiplié par le facteur correct (dans l'agrégat) pour la positionner.

La partie fractionnée est encore plus intéressante:

fraction' :: Integer -> Parser Double
fraction' base =
    do { digits <- many1 ( oneOf ( sigilRange base ))
       ; let base' = fromIntegral base
       ; let f = foldr (\d x -> (x + fromIntegral (convertDigit base d))/base') 0.0 digits
       ; seq f (return f)

Même idée générale, mais maintenant a foldr et en utilisant une division répétée. Je ne comprends pas bien pourquoi vous ajoutez d'abord, puis divisez-vous pour la fraction, mais multipliez d'abord puis ajoutez-le pour le tout. Je sais que ça marche, je n'ai pas triché pourquoi.

Quoi qu'il en soit, je me sens stupide ne pas travailler moi-même, c'est très simple et intelligent le regarder. Y a-t-il un nom pour cet algorithme? Peut-être que la version impérative utilisant une boucle serait plus familière?

5
CodexArcanum

Mon Haskell est très rouillé, mais de savoir ce que je peux voir, c'est la méthode habituelle rapide et facile de convertir une chaîne à un numéro de point flottant. Quick et facile, mais pas une bonne méthode de code de production nécessitant des nombres précis. Chaque division de chaque chiffre dans la fraction provoque une perte potentielle de, IIRC, 1/2 du bit le moins significatif de la mantissie dans la précision. Je ne connais pas les internes des bibliothèques UNIX et Windows, mais je me souviens de voir la microfiche pour des portions des bibliothèques de mathématiques VAX/VMS de retour dans les années 1980 et utilisées, à nouveau, si je me souviens bien, a mis en œuvre une arithmétique de 128 bits correctement implémentée. (qui n'a pas été pris en charge à ce moment-là dans le matériel) pour assurer la précision de leurs résultats en tant que calculs progressés.

1
Alex Measday