swift: problème de conversion de string en double

Voici un code simple dans Xcode 7.3.1 aire de jeux:

var str = "8.7" print(Double(str))

la sortie est surprenante: Optional(8.6999999999999993)

aussi, Float(str) donne: 8.69999981

Des pensées ou des raisons sur ce gars? Toute reference à ceci serait appréciée.

Aussi, comment puis-je convertir "8.7" en 8.7 en tant que Double (ou Float)?

modifier

dans swift:

(str comme NSSsortingng) .doubleValue renvoie 8.7

Maintenant, c'est ok. Mais ma question, encore, n'obtient pas une réponse complète. Nous avons trouvé une alternative mais pourquoi ne pouvons-nous pas countr sur Double ("8.7"). S'il vous plaît, donnez un aperçu plus profond à ce sujet.

Modifier 2

("6.9" comme NSSsortingng) .doubleValue // imprime 6.9000000000000004

Donc, la question s'ouvre à nouveau.

Il y a deux problèmes différents ici. D'abord – comme déjà mentionné dans les commentaires – un nombre à floating point binary ne peut pas représenter le nombre 8.7 précisément. Swift utilise la norme IEEE 754 pour représenter les nombres à floating point simple et double précision, et si vous affectez

 let x = 8.7 

alors le nombre représentable le plus proche est stocké dans x , et c'est

 8.699999999999999289457264239899814128875732421875 

Beaucoup plus d'informations à ce sujet peuvent être trouvées dans l'excellente Q & A Mathématiques à floating point est cassé? .


Le deuxième problème est: Pourquoi le numéro est-il parfois imprimé en "8.7" et parfois en "8.6999999999999993"?

 let str = "8.7" print(Double(str)) // Optional(8.6999999999999993) let x = 8.7 print(x) // 8.7 

Est-ce que Double("8.7") différent de 8.7 ? Est-ce que l'un est plus précis que l'autre?

Pour répondre à ces questions, nous devons savoir comment fonctionne la fonction print() :

  • Si un argument est conforme à CustomSsortingngConvertible , la fonction d'printing appelle sa propriété description et imprime le résultat dans la sortie standard.
  • Sinon, si un argument est conforme à CustomDebugSsortingngConvertible , la fonction d'printing appelle debugDescription et debugDescription le résultat dans la sortie standard.
  • Sinon, un autre mécanisme est utilisé. (Pas importé ici pour notre but.)

Le type Double est conforme à CustomSsortingngConvertible , donc

 let x = 8.7 print(x) // 8.7 

produit la même sortie que

 let x = 8.7 print(x.description) // 8.7 

Mais que se passe-t-il

 let str = "8.7" print(Double(str)) // Optional(8.6999999999999993) 

Double(str) est facultatif et struct Optional n'est pas conforme à CustomSsortingngConvertible , mais à CustomDebugSsortingngConvertible . Par conséquent, la fonction d'printing appelle la propriété debugDescription de Optional , qui à son tour appelle la debugDescription du Double sous-jacent. Par conséquent – en plus d'être un optionnel – le nombre de sortie est le même que dans

 let x = 8.7 print(x.debugDescription) // 8.6999999999999993 

Mais quelle est la différence entre description et debugDescription pour les valeurs à floating point? À partir du code source Swift, on peut voir que les deux appellent finalement la fonction swift_floatingPointToSsortingng dans Stubs.cpp , avec le paramètre Debug défini sur false et true , respectivement. Cela contrôle la précision du nombre à convertir la string:

  int Precision = std::numeric_limits<T>::digits10; if (Debug) { Precision = std::numeric_limits<T>::max_digits10; } 

Pour la signification de ces constantes, voir http://en.cppreference.com/w/cpp/types/numeric_limits :

  • digits10 – nombre de numbers décimaux pouvant être représentés sans changement,
  • max_digits10 – nombre de numbers décimaux nécessaires pour différencier toutes les valeurs de ce type.

Ainsi, la description crée une string avec less de numbers décimaux. Cette string peut être convertie en Double et revenir à une string donnant le même résultat. debugDescription crée une string avec plus de numbers décimaux, de sorte que deux valeurs à floating point différentes produisent une sortie différente.


Résumé:

  • La plupart des nombres décimaux ne peuvent pas être représentés exactement comme une valeur à floating point binary.
  • Les methods description et debugDescription des types floating point utilisent une précision différente pour la conversion en string. En conséquence,
  • l'printing d'une valeur à floating point facultative utilise une précision différente pour la conversion que l'printing d'une valeur non facultative.

Par conséquent, dans votre cas, vous voulez probablement déballer l'option avant de l'imprimer:

 let str = "8.7" if let d = Double(str) { print(d) // 8.7 } 

Pour un meilleur contrôle, utilisez NSNumberFormatter ou une printing formatée avec le format %.<precision>f .

Une autre option peut être d'utiliser (NS)DecimalNumber au lieu de Double (par exemple pour les montants en devise), voir par exemple Round Issue dans swift .

J'utiliserais:

 let doubleValue = NSNumberFormatter().numberFromSsortingng(str)?.doubleValue 

Vous pouvez utiliser ce code peut être utile.

 print(str.doubleValue)