cocoa cocoa-touch rounding currency

cocoa - ¿Debería usar NSDecimalNumber para manejar el dinero?



cocoa-touch rounding (6)

(Adaptado de mi comentario en la otra respuesta).

Si deberías. Una cantidad integral de centavos funciona siempre y cuando no necesite representar, digamos, medio centavo. Si eso sucede, puedes cambiarlo para contar medio centavo, pero ¿y si luego necesitas representar un cuarto de centavo o un octavo de centavo?

La única solución adecuada es NSDecimalNumber (o algo así), que pospone el problema a 10 ^ -128 ¢ (es decir,
0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000001 ¢).

(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 LGPL. Nunca he usado esa biblioteca y no sé exactamente cómo funciona, así que no podría decir qué tan bien funcionaría para usted.)

[Editar: Aparentemente, al menos una persona, Brad Larson, cree que estoy hablando de punto flotante binario en alguna parte de esta respuesta. No soy.]

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

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

Tenga en cuenta 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é que podría preguntar aquí. Tal vez la respuesta sea obvia para la mayoría, pero quiero estar seguro antes de comenzar un re-factoring masivo de mi aplicación.

Convenceme :)


Marcus Zarra tiene una postura bastante clara al respecto: "Si está tratando con dinero en absoluto, entonces debería usar NSDecimalNumber". Su artículo me inspiró a mirar NSDecimalNumber, y me ha impresionado mucho. Los errores de coma flotante de IEEE cuando se trata de matemática de 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 coma flotante binaria, sino que realiza matemáticas 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 precisión de más de 30 dígitos decimales y ningún error de punto flotante extraño podría ser una excepción, pero creo que vale la pena analizarlo. Las operaciones son un poco más incómodas que el simple var = 1 + 2 estilo matemático, pero todavía son manejables. Si le preocupa asignar todo tipo de instancias durante sus operaciones matemáticas, NSDecimal es el equivalente de C struct de NSDecimalNumber y hay funciones C para realizar exactamente las mismas operaciones matemáticas con él. En mi experiencia, estos son bastante rápidos para todas las aplicaciones menos exigentes (3,344,593 adiciones / s, 254,017 divisiones / s en una MacBook Air, 281,555 adiciones / s, 12,027 divisiones / s en un iPhone).

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


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


Sí. Tienes que usar

NSDecimalNumber y

no duplicar o flotar cuando maneja la divisa en iOS.

¿¿Porqué 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 utilizado por las computadoras para almacenar decimales causa este error de enrutamiento. Si necesita más detalles, lea

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

De acuerdo con 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 longitud, y exponente es un número entero de -128 a 127.wrapper para hacer base-10 aritmética.

Así NSDecimalNumber se recommonded para tratar con la moneda.


Una mejor pregunta es, ¿cuándo no debería usar NSDecimalNumber para manejar 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 se trata de más de unos pocos dígitos de precisión. La respuesta aún más breve es que siempre debe usar NSDecimalNumber cuando se trata de dinero.


VISA, MasterCards y otros están usando valores enteros mientras pasan cantidades. Dependerá del emisor y el receptor analizar los totales correctamente según el exponente de la divisa (dividir o multiplicar por 10 ^ num, donde num - es un exponente de la divisa). 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 exponente = 0 (VND, etc.) o = 3.