核心数据和 NSDecimalNumber

Coredata and NSDecimalNumber

我对存储在核心数据中的小数有疑问。当我将数字 0.6789 保存到数据库中时,它被保存为 0.6788999999999999。

我在某处读到,建议将小数列设置为小数以保持精度,因为核心数据会自动处理核心数据中小数列的 NSDecimalNumber。

下面是我的实体class:

@interface TestEntity : NSManagedObject
@property (nonatomic, retain) NSDecimalNumber * cost;
@end

@implementation TestEntity
@dynamic cost;
@end

这是我插入数据的方式:

TestEntity *envelope = [NSEntityDescription insertNewObjectForEntityForName:@"TestEntity" inManagedObjectContext:self.managedObjectContext];
envelope.cost = [NSDecimalNumber decimalNumberWithString:@"0.6789"];
[self.managedObjectContext save:nil];

请大神帮帮我,插入数据库时​​应该如何处理十进制数及其精度?

作为 CoreData 后端的 SQLite 不支持小数,而是使用浮点数。


来自 SQLite 文档:

Each column in an SQLite 3 database is assigned one of the following type affinities: TEXT NUMERIC INTEGER REAL BLOB

更多详细信息请参阅 SQLite 文档 https://sqlite.org/datatype3.html 3.1.1。亲缘关系名称示例。

核心数据由 SQLite 支持。根据 SQLite documentation SQLite only supports integers up to 64 bit and floating point numbers with the 64bit IEEE 754 格式(Objective C 中的 double)。所以精度仅限于这些类型,整数约为18.75位有效数字,浮点数约为15.75位。

通常 doubleint64_t 适用于大多数应用程序,但需要超过 15-18 位有效数字的精度除外。在某些司法管辖区,银行机构必须依法存储高精度的十进制数,为此他们使用特殊类型。

如果您的应用程序是标准的,我会使用 int64_tdouble。如果你使用 NSDecimalNumber 它将被 SQLite 引擎存储为 64 位类型(浮点数或整数取决于小数部分的存在)。这意味着您的精度将限制在 15-18 位数字。

如果你想使用NSDecimalNumber因为它的高精度算法,你仍然会被限制在15/18位,但如果你的dynamic range可以表达,就不会引入舍入误差作为 15/18 位数字(带或不带小数部分)。

如果您的动态范围更大,NSDecimalNumber 提供 38 位数字。为了将它存储在 Core Data SQLite 支持的持久存储中并能够保持 38 位数字,您应该将它来回转换为字符串,以便它作为字符串存储在 Core Data 中。您可以在 NSManagedObject 的子类上创建一个类别,并使用几种方法将数字存储(编码)和检索(解码)为字符串。模型中的基础 属性 应该是字符串,而不是数字。

NSDecimalNumber 有两种方法 -stringValue-initWithString: 可以为您进行转换。您只需要在您的自定义类别中构造两个依赖于这些的方法。

总结:

  • 如果需要小数,请使用 double 表示 15 位有效数字
  • 如果您不需要小数,请使用 int64_t 表示 18 位有效数字
  • 如果需要精度高的十进制数,38位有效数字使用NSDecimalNumber(需要转换成字符串存入CoreData)