Domanda

Quando ho iniziato a scrivere la mia prima app, ho usato NSNumber per i valori in denaro senza pensarci due volte. Poi ho pensato che forse i tipi c erano sufficienti per gestire i miei valori. Tuttavia, nel forum SDK di iPhone mi è stato consigliato di utilizzare NSDecimalNumber, a causa delle sue eccellenti capacità di arrotondamento.

Non essendo un matematico per temperamento, ho pensato che il paradigma mantissa / esponente potesse essere eccessivo; tuttavia, cercando su Google, mi sono reso conto che la maggior parte dei discorsi su denaro / valuta nel cacao erano riferiti a NSDecimalNumber.

Nota che l'app su cui sto lavorando sarà internazionalizzata, quindi l'opzione di contare l'importo in centesimi non è realmente praticabile, poiché la struttura monetaria dipende molto dal locale utilizzato.

Sono sicuro al 90% di dover andare con NSDecimalNumber, ma poiché non ho trovato risposte inequivocabili sul Web (qualcosa del tipo: " se hai a che fare con i soldi, usa NSDecimalNumber! ") Ho pensato di chiedere Qui. Forse la risposta è ovvia per la maggior parte, ma voglio essere sicuro prima di iniziare un massiccio re-factoring della mia app.

Convincimi :)

È stato utile?

Soluzione

Marcus Zarra ha una posizione abbastanza chiara su questo: " Se hai a che fare con la valuta, allora dovresti usare NSDecimalNumber. " Il suo articolo mi ha ispirato a esaminare NSDecimalNumber, e ne sono rimasto molto colpito esso. Gli errori IEEE in virgola mobile quando ho a che fare con la matematica di base 10 mi hanno irritato per un po '(1 * (0,5 - 0,4 - 0,1) = -0,00000000000000002776) e NSDecimalNumber li elimina.

NSDecimalNumber non si limita ad aggiungere qualche altra cifra di precisione binaria in virgola mobile, ma in realtà esegue la matematica base-10. Questo elimina gli errori come quello mostrato nell'esempio sopra.

Ora sto scrivendo un'applicazione matematica simbolica, quindi il mio desiderio di una precisione di oltre 30 cifre decimali e nessuno strano errore in virgola mobile potrebbe essere un'eccezione, ma penso che valga la pena di guardarlo. Le operazioni sono un po 'più complicate della semplice matematica in stile var = 1 + 2, ma sono comunque gestibili. Se sei preoccupato di allocare tutti i tipi di istanze durante le tue operazioni matematiche, NSDecimal è l'equivalente C struct di NSDecimalNumber e ci sono funzioni C per eseguire esattamente le stesse operazioni matematiche con esso. Nella mia esperienza, queste sono molto veloci per tutte le applicazioni tranne quelle più esigenti (3.344.593 aggiunte / s, 254.017 divisioni / e su un MacBook Air, 281.555 aggiunte / s, 12.027 divisioni / s su un iPhone).

Come bonus aggiuntivo, la descrizione di NSDecimalNumberWithLocale: method fornisce una stringa con una versione localizzata del numero, incluso il separatore decimale corretto. Lo stesso vale al contrario per initWithString: locale: method.

Altri suggerimenti

Sì. Devi usare

NSDecimalNumber e

non doppio o float quando gestisci valuta su iOS.

Perché è ??

Perché non vogliamo ottenere cose come $ 9.9999999998 invece di $10

Come succede ??

I float e i doppi sono approssimazioni. Viene sempre fornito un errore di arrotondamento. Il formato utilizzato dai computer per memorizzare i decimali causa questo errore di rouding. Se hai bisogno di maggiori dettagli leggi

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

Secondo i documenti di Apple,

NSDecimalNumber è una sottoclasse immutabile di NSNumber, fornisce un wrapper orientato agli oggetti per eseguire l'aritmetica di base 10. Un'istanza può rappresentare qualsiasi numero che può essere espresso come mantissa x 10 ^ esponente in cui mantissa è un numero intero decimale lungo fino a 38 cifre e l'esponente è un numero intero compreso tra & # 8211; 128 e 127. wrapper per eseguire l'aritmetica di base 10.

Quindi NSDecimalNumber è raccomandato per trattare con la valuta.

(Adattato dal mio commento sull'altra risposta.)

Sì, dovresti. Un numero intero di penny funziona solo fino a quando non è necessario rappresentare, diciamo, mezzo centesimo. Se ciò accade, potresti cambiarlo per contare i mezzo centesimi, ma cosa succederebbe se dovessi rappresentare un quarto di centesimo o un ottavo di centesimo?

L'unica soluzione corretta è NSDecimalNumber (o qualcosa del genere), che rimanda il problema a 10 ^ -128 & # 162; (Vale a dire,
0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000001 & # 162;).

(Un altro modo sarebbe l'aritmetica di precisione arbitraria, ma ciò richiede una libreria separata, come la libreria GNU MP Bignum . GMP è sotto la LGPL. Non ho mai usato quella libreria e non so esattamente come funzioni, quindi non potrei dire quanto funzionerebbe bene per te.)

[Modifica: Apparentemente, almeno una persona & # 8212; Brad Larson & # 8212; pensa che sto parlando di virgola mobile binaria da qualche parte in questa risposta. Non lo sono.]

Una domanda migliore è: quando non usare NSDecimalNumber per gestire i soldi. La risposta breve a questa domanda è quando non puoi tollerare il sovraccarico prestazionale di NSDecimalNumber e non ti importa di piccoli errori di arrotondamento perché non hai mai a che fare con più di poche cifre di precisione. La risposta ancora più breve è che dovresti sempre utilizzare NSDecimalNumber quando hai a che fare con denaro.

Ho trovato comodo usare un numero intero per rappresentare il numero di centesimi e poi dividere per 100 per la presentazione. Evita l'intero problema.

VISA, MasterCard e altri utilizzano valori interi durante il passaggio degli importi. Spetta al mittente e al destinatario ricevere un'analisi corretta in base all'esponente della valuta (dividere o moltiplicare per 10 ^ num, dove num - è un esponente della valuta). Nota che valute diverse hanno esponenti diversi. Di solito è 2 (quindi dividiamo e moltiplichiamo per 100), ma alcune valute hanno esponente = 0 (VND, ecc.) O = 3.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top