I am carrying out a number of calculations using NSDecimal
and am creating each NSDecimal
struct using the following technique:
[[NSNumber numberWithFloat:kFloatConstant] decimalValue]
I am using NSDecimal
to avoid using autoreleased NSDecimalNumber
objects (if the NSDecimalNumber
approach to accurate calculations is used). However it seems that the NSNumber
creation mechanism also returns an autoreleased NSNumber
from which the decimal value is extracted.
Is there a way to create an NSDecimal
without using NSNumber and creating autoreleased objects?
Unfortunately, Apple does not provide any easy ways of putting values into an NSDecimal struct. The struct definition itself can be found in the NSDecimal.h header:
typedef struct {
signed int _exponent:8;
unsigned int _length:4; // length == 0 && isNegative -> NaN
unsigned int _isNegative:1;
unsigned int _isCompact:1;
unsigned int _reserved:18;
unsigned short _mantissa[NSDecimalMaxSize];
} NSDecimal;
but I don't know that I would go around trying to reverse-engineer how the structs hold values. The underscores on the fields in the struct indicate that these are private and subject to change. I don't imagine that a lot of changes occur in the low-level NSDecimal functions, but I'd be nervous about things breaking at some point.
Given that, initializing an NSDecimal from a floating point number is best done in the way that you describe. However, be aware that any time you use a floating point value you are losing the precision you've gained by using an NSDecimal, and will be subjecting yourself to floating point errors.
I always work only with NSDecimals within my high-precision calculations, and take in and export NSStrings for exchanging these values with the outside world. To create an NSDecimal based on an NSString, you can use the approach we take in the Core Plot framework:
NSDecimal CPDecimalFromString(NSString *stringRepresentation)
{
NSDecimal result;
NSScanner *theScanner = [[NSScanner alloc] initWithString:stringRepresentation];
[theScanner scanDecimal:&result];
[theScanner release];
return result;
}
Using an NSScanner instead of NSDecimalNumber -initWithString:locale:
is about 90% faster in my benchmarks.
To answer the “without autoreleased objects” part, simply:
NSDecimalNumber *intermediateNumber = [[NSDecimalNumber alloc] initWithFloat:kFloatConstant];
NSDecimal decimal = [intermediateNumber decimalValue];
[intermediateNumber release];