ios - ven - rápido: problema en la conversión de cadena a doble
porque no puedo subir estados en whatsapp (3)
Puede usar este código puede ser útil.
print(str.doubleValue)
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.
Yo usaría:
let doubleValue = NSNumberFormatter().numberFromString(str)?.doubleValue
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 dedescription
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 propiedaddebugDescription
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<T>::digits10;
if (Debug) {
Precision = std::numeric_limits<T>::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étodosdebugDescription
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 %.<precision>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 .