2010-02-01 18 views
9

J'essaye d'écrire une fonction dans le langage de programmation D pour remplacer les appels au strtold de C. (Justification: Pour utiliser strtold à partir de D, vous devez convertir les chaînes D en chaînes C, ce qui est inefficace.) Strtold ne peut pas être exécuté au moment de la compilation.) J'ai mis au point une implémentation qui fonctionne principalement, mais je semblent perdre de la précision dans les bits les moins significatifs.Comment convertir les chaînes en flotteurs avec une précision parfaite?

Le code de la partie intéressante de l'algorithme est ci-dessous et je peux voir d'où vient la perte de précision, mais je ne sais pas comment m'en débarrasser. (J'ai omis beaucoup de parties du code qui n'étaient pas pertinentes pour l'algorithme de base pour sauver les gens qui lisent.) Quel algorithme de chaîne à flotteur garantira que le résultat sera le plus proche possible du numéro IEEE ligne à la valeur représentée par la chaîne.

real currentPlace = 10.0L ^^ (pointPos - ePos + 1 + expon); 

real ans = 0; 
for(int index = ePos - 1; index > -1; index--) { 
    if(str[index] == '.') { 
     continue; 
    } 

    if(str[index] < '0' || str[index] > '9') { 
     err(); 
    } 

    auto digit = cast(int) str[index] - cast(int) '0'; 
    ans += digit * currentPlace; 
    currentPlace *= 10; 
} 

return ans * sign; 

Aussi, j'utilise les tests unitaires pour l'ancienne version, qui a fait des choses comme:

assert(to!(real)("0.456") == 0.456L); 

Est-il possible que les réponses étant produites par ma fonction sont en fait plus précis que la représentation que le compilateur produit lors de l'analyse d'un littéral en virgule flottante, mais le compilateur (écrit en C++) est toujours en accord avec strtold car il utilise strtold en interne pour analyser les littéraux en virgule flottante?

+0

veuillez indiquer d'où vous pensez que provient la perte de précision. –

+0

@John: La perte de précision vient de deux endroits: 1. Nous arrondissons chaque fois que nous exécutons la ligne 'ans + = digit * currentPlace', et 10^je ne peux pas être représenté exactement dans IEEE 754 pour la plupart des entiers i. – dsimcha

+0

Pour référence, consultez les implémentations de strtod: http://www.google.com/codesearch?hl=fr&lr=&q=double+strtod+const+char+str%2C+char+endptr&sbtn=Recherche – outis

Répondre

0

Vous ne pouvez pas stocker la plupart des flotteurs avec une précision parfaite dans un ordinateur numérique

+0

Droite. C'est pourquoi je définis la précision "parfaite" comme le nombre à virgule flottante (32/64/80) le plus proche du nombre représenté par la chaîne d'entrée. – dsimcha

9

Clinger et Steele & White a développé des algorithmes fins pour la lecture et l'écriture à virgule flottante.

Il existe une rétrospective here avec quelques références aux implémentations.

Le travail de Clinger est amélioré par de David Gay, et le implementation in C de Gay est génial. Je les ai utilisés dans les systèmes embarqués, et je crois que dtoa de Gay a fait son chemin dans de nombreux libc.

+0

Le code source de Gay contient sa propre implémentation BigInt qu'il utilise dans certains de ses calculs temporaires, c'est un peu de code. Je pense que le papier est beaucoup plus facile à lire. –

+0

Le dtoa de Gay est utilisé partout: Firefox, Opera, Safari, Thunderbird, KDE, Chrome, Python, MySQL, Mac OS X, J et D, par exemple. –

+2

Depuis que vous avez écrit cette réponse, un algorithme beaucoup plus efficace que celui de Gay a été découvert pour imprimer les valeurs canoniques les plus courtes pour les nombres à virgule flottante! Peut-être mettre à jour votre réponse pour refléter cela? Voir "Impression de nombres à virgule flottante rapidement et précisément avec entiers" ici: http://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf –

1

Commencez par accumuler les chiffres sous la forme d'un nombre entier, en ignorant le point décimal et l'exposant. Vous utiliseriez toujours un accumulateur à virgule flottante, mais vous n'auriez aucune partie fractionnaire, ceci évite la perte de précision en raison de l'impossibilité d'exprimer exactement les nombres à virgule flottante. (vous devez également ignorer les chiffres fractionnaires qui dépassent la précision des flottants pour représenter - 8 chiffres pour les flottants IEEE 32 bits).

Vous pouvez utiliser un entier de 64 bits pour accumuler des chiffres si vous préférez mais vous devez faire attention d'ignorer les chiffres supplémentaires qui provoqueraient un débordement si vous le faites. (vous devrez peut-être tenir compte de ces chiffres lors de la détermination de l'exposant)

Ensuite, mettez cette valeur à l'échelle de l'exposant, en tenant compte de l'emplacement du point décimal que vous avez ignoré lors de l'accumulation des chiffres.

0

Vous créez un nombre à virgule flottante pour chaque chiffre, puis vous ajoutez ces nombres ensemble. Puisque les nombres à virgule flottante ne sont pas exacts, mais arrondis à un certain nombre de chiffres binaires, cela implique de petites imprécisions lors du stockage des nombres uniques et de leur addition. Par conséquent, l'addition des nombres à virgule flottante pour les chiffres uniques peut donner un résultat avec une petite erreur d'arrondi.

Un exemple serait 0.1 + 0.02, qui est pas égale à 0.12 si elle est représentée en tant que nombre à virgule flottante.(Pour vérifier cela, essayez de les comparer dans votre langage de programmation préféré)

2

Honnêtement, c'est une de ces choses que vous ne devriez vraiment pas faire si vous ne savez pas déjà comment le faire. Il est plein d'embûches, et même si vous parvenez à le faire correctement, il sera probablement très lent si vous n'avez pas d'expertise dans l'analyse des performances numériques de bas niveau. Cela dit, si vous êtes vraiment déterminé à écrire votre propre implémentation, la meilleure référence pour l'exactitude est "Conversions Binary-Decimal et Decimal-Binary correctement arrondies" de David Gay (postscript version). Vous devriez également étudier ses implémentations de référence (en C), qui sont disponibles sur Netlib.