Reputation: 2181
I'm having issue with decimal numbers stored in core data. When I'm saving number 0.6789 into database it is saved as 0.6788999999999999.
I have read somewhere that it is recommended to have decimal number columns as decimal to maintain the precision as core data automatically handles NSDecimalNumber for decimal columns in core data.
Below is my entity class:
@interface TestEntity : NSManagedObject
@property (nonatomic, retain) NSDecimalNumber * cost;
@end
@implementation TestEntity
@dynamic cost;
@end
This is how I'm inserting data:
TestEntity *envelope = [NSEntityDescription insertNewObjectForEntityForName:@"TestEntity" inManagedObjectContext:self.managedObjectContext];
envelope.cost = [NSDecimalNumber decimalNumberWithString:@"0.6789"];
[self.managedObjectContext save:nil];
Please someone help me on, how should I handle the decimal numbers and their precision when inserted into database?
Upvotes: 1
Views: 1637
Reputation: 671
SQLite which is backend for CoreData has no support for decimals and float is used instead.
From SQLite documentation:
Each column in an SQLite 3 database is assigned one of the following type affinities: TEXT NUMERIC INTEGER REAL BLOB
More details are at SQLite documentation https://sqlite.org/datatype3.html 3.1.1. Affinity Name Examples.
Upvotes: 2
Reputation: 2309
Core Data is backed by SQLite. According to the SQLite documentation SQLite only supports integers up to 64 bit and floating point numbers with the 64bit IEEE 754 format (double
in Objective C). So the precision is limited to these types, which is about 18.75 significant digits for integers and 15.75 digits for floating point numbers.
Usually a double
or int64_t
are right for most of the applications except for those where precision beyond 15-18 significant digits is required. In some jurisdictions, banking institutions have to store decimal numbers with high precision by law and they use special types for this.
If your application is standard I would use int64_t
or double
. If you use NSDecimalNumber
it is going to be stored as a 64-bit type by the SQLite engine (floating point or integer depending on the existence of a fractional part). This means your precision will be limited to 15-18 digits.
If you want to use NSDecimalNumber
because of its high precision arithmetic, you still will be limited to 15/18-digits but rounding errors will not be introduced if your dynamic range can be expressed as a 15/18-digit number (with fractional part or without).
If your dynamic range is greater, NSDecimalNumber
provides 38 digits. In order to store it in a Core Data SQLite-backed persistent storage and be able to maintain the 38 digits, you should convert it to string, back and forth, so that it gets stored in Core Data as a string. You could create a category on your subclass of NSManagedObject
with a couple of methods to store (encode) and retrieve (decode) the number to a string. The underlying property in your model should the be a string, instead of a number.
NSDecimalNumber
has two methods -stringValue
and -initWithString:
to do the conversion for you. You just need to construct two methods that rely on these in your custom category.
In summary:
double
for 15 significant digitsint64_t
for 18 significant digitsNSDecimalNumber
for 38 significant digits (with the conversion to string for storage in CoreData)Upvotes: 6