I'm using NSDecimalNumber to store a value for currency. I'm trying to write a method called "cents" which returns the decimal portion of the number as an NSString with a leading 0 if the number is < 10. So basically
NSDecimalNumber *n = [[NSDecimalNumber alloc] initWithString:@"1234.55"];
NSString *s = [object cents:n];
And I'm trying to craft a method that will return 01, 02, etc...up to 99 as a string. I can't seem to figure out how to return the mantissa as a string in this format. I feel like I'm missing a convenience method but I looked at the documentation again and don't see anything jumping out at me.
Update: This is a relatively old answer, but it looks like people are still finding it. I want to update this for correctness — I originally answered the question as I did simply to demonstrate how one could pull out specific decimal places from a double
, but I do not advocate this as a way to represent currency information.
Never use floating-point numbers to represent currency information. As soon as you start dealing with decimal numbers (as you would with dollars with cents), you introduce possible floating point errors into your code. A computer cannot represent all decimal values accurately (1/100.0
, for instance, 1 cent, is represented as 0.01000000000000000020816681711721685132943093776702880859375
on my machine). Depending on which currencies you plan on representing, it is always more correct to store a quantity in terms of its base amount (in this case, cents).
If you store your dollar values in terms of integral cents, you'll never run into floating-point errors for most operations, and it's trivially easy to convert cents into dollars for formatting. If you need to apply tax, for instance, or multiply your cents value by a double
, do that to get a double
value, apply banker's rounding to round to the nearest cent, and convert back to an integer.
It gets more complicated than that if you're trying to support multiple different currencies, but there are ways of dealing with that.
tl;dr Don't use floating-point numbers to represent currency and you'll be much happier, and more correct. NSDecimalNumber
is able to accurately (and precisely) represent decimal values, but as soon as you convert to double
/float
, you run the risk of introducing floating-point errors.
This can be done relatively easily:
double
value of the decimal number (this will work assuming the number is not too large to store in a double).double
to an integer.(This is a general formula for working with all decimal numbers – working with NSDecimalNumber
just requires a little bit of glue code to get it to work).
In practice, it would look like this (in an NSDecimalNumber
category):
- (NSString *)cents {
double value = [self doubleValue];
unsigned dollars = (unsigned)value;
unsigned cents = (value * 100) - (dollars * 100);
return [NSString stringWithFormat:@"%02u", cents];
}