rápido: problema en la conversión de cadena a doble

Aquí hay un código simple en el patio de Xcode 7.3.1:

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

la salida es sorprendente: Optional(8.6999999999999993)

también, Float(str) da: 8.69999981

¿Alguna idea o razón sobre este tipo? Cualquier referencia a esto sería apreciada.

Además, ¿cómo debo convertir “8.7” a 8.7 como Doble (o Float)?

Editar

en rápido:

(str como NSString) .doubleValue devuelve 8.7

Ahora, eso está bien. Pero mi pregunta, todavía, no obtiene una respuesta completa. Hemos encontrado una alternativa, pero ¿por qué no podemos confiar en Double (“8.7”). Por favor, den una visión más profunda de esto.

Editar 2

(“6.9” como NSString) .doubleValue // imprime 6.9000000000000004

Entonces, la pregunta se abre de nuevo.

Hay dos problemas diferentes aquí. Primero, como ya se mencionó en los comentarios, un número de coma flotante binario no puede representar el número 8.7 precisión. Swift usa el estándar IEEE 754 para representar números de punto flotante de precisión simple y doble, y si asigna

 let x = 8.7 

entonces el número representable más cercano se almacena en x , y eso es

 8.699999999999999289457264239899814128875732421875 

Se puede encontrar mucha más información acerca de esto en las excelentes preguntas y respuestas. ¿Las matemáticas de coma flotante están rotas? .


El segundo problema es: ¿por qué el número a veces se imprime como “8.7” y algunas veces como “8.6999999999999993”?

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

¿Es el Double("8.7") diferente de 8.7 ? ¿Es uno más preciso que el otro?

Para responder estas preguntas, necesitamos saber cómo funciona la función print() :

  • Si un argumento se ajusta a CustomStringConvertible , la función de impresión llama a su propiedad de description e imprime el resultado a la salida estándar.
  • De lo contrario, si un argumento se ajusta a CustomDebugStringConvertible , la función de impresión llama a la propiedad debugDescription e imprime el resultado a la salida estándar.
  • De lo contrario, se usa algún otro mecanismo. (No importado aquí para nuestro propósito)

El tipo Double ajusta a CustomStringConvertible , por lo tanto

 let x = 8.7 print(x) // 8.7 

produce el mismo resultado que

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

Pero que pasa en

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

Double(str) es un opcional , y struct Optional no se ajusta a CustomStringConvertible , sino a CustomDebugStringConvertible . Por lo tanto, la función de impresión llama a la propiedad debugDescription de Optional , que a su vez llama a debugDescription del Double subyacente. Por lo tanto, además de ser una opción, el resultado del número es el mismo que en

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

Pero, ¿cuál es la diferencia entre description y debugDescription para valores de coma flotante? Del código fuente de Swift se puede ver que ambos llaman a la función swift_floatingPointToString en Stubs.cpp , con el parámetro Debug establecido en false y true , respectivamente. Esto controla la precisión del número a la conversión de cadena:

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

Para conocer el significado de esas constantes, consulte http://en.cppreference.com/w/cpp/types/numeric_limits :

  • digits10 – número de dígitos decimales que se pueden representar sin cambios,
  • max_digits10 – número de dígitos decimales necesarios para diferenciar todos los valores de este tipo.

Entonces la description crea una cadena con menos dígitos decimales. Esa secuencia se puede convertir en un Double y volver a una cadena que da el mismo resultado. debugDescription crea una cadena con más dígitos decimales, de modo que dos valores diferentes de punto flotante producirán una salida diferente.


Resumen:

  • La mayoría de los números decimales no se pueden representar exactamente como un valor de punto flotante binario.
  • La description y los métodos debugDescription de los tipos de coma flotante usan una precisión diferente para la conversión a una cadena. Como consecuencia,
  • imprimir un valor de coma flotante opcional utiliza una precisión diferente para la conversión que imprimir un valor no opcional.

Por lo tanto, en su caso, probablemente quiera desenvolver la opción opcional antes de imprimirla:

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

Para un mejor control, use NSNumberFormatter o la impresión formateada con el formato %.f .

Otra opción puede ser utilizar (NS)DecimalNumber lugar de Double (p. Ej., Para cantidades de moneda), ver, por ejemplo, Round Issue in swift .

Yo usaría:

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

Puede usar este código puede ser útil.

 print(str.doubleValue)