Pregunta

Cuando comencé a codificar mi primera aplicación, utilicé NSNumber para obtener valores monetarios sin pensarlo dos veces. Entonces pensé que tal vez los tipos c fueran suficientes para lidiar con mis valores. Sin embargo, en el foro de SDK de iPhone se me recomendó usar NSDecimalNumber, debido a sus excelentes capacidades de redondeo.

Al no ser matemático por temperamento, pensé que el paradigma de la mantisa / exponente podría ser una exageración; aún así, googlin 'alrededor, me di cuenta de que la mayoría de las conversaciones sobre dinero / moneda en cacao se referían a NSDecimalNumber.

Observe que la aplicación en la que estoy trabajando se internacionalizará, por lo que la opción de contar la cantidad en centavos no es realmente viable, ya que la estructura monetaria depende en gran medida de la configuración regional utilizada.

Estoy 90% seguro de que necesito ir con NSDecimalNumber, pero como no encontré una respuesta inequívoca en la web (algo así como: "si manejas dinero, ¡usa NSDecimalNumber!") pensé en preguntar aquí. Tal vez la respuesta sea obvia para la mayoría, pero quiero estar seguro antes de comenzar a modificar mi aplicación de forma masiva.

Convénceme :)

¿Fue útil?

Solución

Marcus Zarra tiene una postura bastante clara sobre esto: " Si está tratando con una moneda, entonces debería usar NSDecimalNumber. " Su artículo me inspiró a buscar en NSDecimalNumber, y estoy muy impresionado con eso. Los errores de punto flotante de IEEE al tratar con matemática base-10 me han estado irritando por un tiempo (1 * (0.5 - 0.4 - 0.1) = -0.00000000000000002776) y NSDecimalNumber los elimina.

NSDecimalNumber no solo agrega otros pocos dígitos de precisión de punto flotante binario, en realidad hace matemática de base 10. Esto elimina los errores como el que se muestra en el ejemplo anterior.

Ahora, estoy escribiendo una aplicación matemática simbólica, por lo que mi deseo de una precisión de más de 30 dígitos decimales y ningún error extraño de coma flotante podría ser una excepción, pero creo que vale la pena mirarlo. Las operaciones son un poco más incómodas que las matemáticas simples de estilo var = 1 + 2, pero aún son manejables. Si le preocupa asignar todo tipo de instancias durante sus operaciones matemáticas, NSDecimal es el equivalente de Cestructura de NSDecimalNumber y hay funciones C para hacer exactamente las mismas operaciones matemáticas con él. En mi experiencia, estas son bastante rápidas para todas, pero las aplicaciones más exigentes (3.344.593 adiciones / s, 254.017 divisiones / s en un MacBook Air, 281.555 adiciones / s, 12.027 divisiones / s en un iPhone).

Como una ventaja adicional, el método de descripción de NSDecimalNumberWithLocale: proporciona una cadena con una versión localizada del número, incluido el separador decimal correcto. Lo mismo ocurre a la inversa para su initWithString: locale: method.

Otros consejos

Sí Tienes que usar

NSDecimalNumber y

no doble o flotante cuando trata con divisas en iOS.

¿Por qué es eso?

Porque no queremos obtener cosas como $ 9.9999999998 en lugar de $10

¿Cómo sucede eso?

Flotadores y dobles son aproximaciones. Siempre vienen con un error de redondeo. El formato que usan las computadoras para almacenar decimales causa este error de enrutamiento. Si necesitas más detalles lee

http://floating-point-gui.de/

Según los documentos de Apple,

NSDecimalNumber es una subclase inmutable de NSNumber, proporciona un contenedor orientado a objetos para hacer aritmética de base 10. Una instancia puede representar cualquier número que se pueda expresar como mantisa x 10 ^ exponente donde mantissa es un entero decimal de hasta 38 dígitos de largo, y el exponente es un entero desde & # 8211; 128 a 127. envoltorio para hacer aritmética de base 10.

Por lo tanto, NSDecimalNumber se recomienda para tratar con divisas.

(Adaptado de mi comentario sobre la otra respuesta)

Sí, deberías. Un número integral de centavos funciona solo mientras no necesite representar, digamos, medio centavo. Si eso sucede, puede cambiarlo para que cuente con medio centavo, pero ¿qué pasa si luego necesita representar un cuarto de centavo, o un octavo de centavo?

La única solución adecuada es NSDecimalNumber (o algo parecido), que pone el problema a 10 ^ -128 & # 162; (es decir,
0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000001 & # 162;).

(Otra forma sería la aritmética de precisión arbitraria, pero eso requiere una biblioteca separada, como la biblioteca GNU MP Bignum . GMP está bajo la licencia LGPL. Nunca he usado esa biblioteca y no sé exactamente cómo funciona, por lo que no podría decir qué tan bien funcionaría para usted.)

[Editar: al parecer, al menos una persona & # 8212; Brad Larson & # 8212; piensa que estoy hablando de punto flotante binario en alguna parte de esta respuesta. No lo estoy.]

Una mejor pregunta es, ¿cuándo debería no usar NSDecimalNumber para lidiar con el dinero? La respuesta breve a esa pregunta es, cuando no puede tolerar la sobrecarga de rendimiento de NSDecimalNumber y no le importan los pequeños errores de redondeo porque nunca está tratando con más de unos pocos dígitos de precisión. La respuesta aún más breve es que debes siempre utilizar NSDecimalNumber cuando se trata de dinero.

Me ha parecido conveniente usar un número entero para representar el número de centavos y luego dividir por 100 para la presentación. Evita todo el problema.

VISA, MasterCards y otros están usando valores enteros al pasar cantidades. Depende del remitente y del receptor analizar las cantidades correctamente de acuerdo con el exponente de la moneda (dividir o multiplicar por 10 ^ num, donde num - es un exponente de la moneda). Tenga en cuenta que las diferentes monedas tienen diferentes exponentes. Por lo general, es 2 (por lo tanto, dividimos y multiplicamos por 100), pero algunas monedas tienen un exponente = 0 (VND, etc.), o = 3.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top