我在double
中有一个NSNumber
。
double myDouble = 1363395572.6129999;
NSNumber *doubleNumber = @(myDouble);
// using [NSNumber numberWithDouble:myDouble] leads to the same result
这就是问题所在。
doubleNumber.doubleValue
似乎返回正确的值(1363395572.6129999)
但是,查看调试器中的doubleNumber
或执行doubleNumber.description
会给我(1363395572.613)
。
我想知道是否这只是一些显示格式化,但是当我将这个对象粘贴到JSON有效载荷中时,会插入混乱的舍入值而不是实际的数字。
我这样做的方式是这样的:
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:(Dictionary containing NSNumber)
options:0 error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
此时查看字符串会显示截断的数字,其中包含3位小数,即使我插入的NSNumber
有7位。
我的问题是为什么会发生这种情况,更重要的是如何阻止它发生?
编辑结论:
对于任何偶然发现这个问题的人来说,问题从一开始我就不清楚了,但实际问题是NSNumber
和double
都无法以精确的方式持有数字我我在找。正如Martin的回答所示,我的问题是在我从JSON响应中反序列化初始数值时发生的。
我最终通过重新处理整个系统来解决我的问题,取决于客户端上这些数字的这种精度级别(因为这些是时间戳,微秒),而是使用不同的标识符来传递API 。
正如Martin和Leo所指出的,为了解决这个问题,我们需要使用一个自定义JSON解析器,它允许将JSON号解析为NSDecimalNumber
而不是NSNumber
。特别是我的问题的一个更好的解决方案是我在前一段中概述的内容,所以我没有采用这条路线。
答案 0 :(得分:9)
正如上面的评论所述,double
的精度约为16位小数
数字。 1363395572.612999
有17位数字,并转换此十进制数字
double
与1363395572.613
的结果完全相同:
double myDouble = 1363395572.6129999;
double myDouble1 = 1363395572.613;
NSLog(@"%.20f", myDouble); // 1363395572.61299991607666015625
NSLog(@"%.20f", myDouble1); // 1363395572.61299991607666015625
NSLog(@"%s", myDouble == myDouble1 ? "equal" : "different"); // equal
因此,在double
的精度范围内,输出1363395572.613
正确。
如果您的目标是准确发送号码“1363395572.6129999”那么您就不能
首先将它存储在double
中,因为它已经失去了精度。
一种可能的解决方案是使用NSDecimalNumber
(具有精度
38位小数):
NSDecimalNumber *doubleNumber = [NSDecimalNumber decimalNumberWithString:@"1363395572.6129999"];
NSDictionary *dict = @{@"key": doubleNumber};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict
options:0 error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
// {"key":1363395572.6129999}
long double
和NSDecimalNumber
的示例:
long double ld1 = 1363395572.6129999L;
long double ld2 = 1363395572.613L;
NSDecimalNumber *num1 = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%.7Lf", ld1]];
NSDecimalNumber *num2 = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%.7Lf", ld2]];
NSDictionary *dict = @{@"key1": num1, @"key2": num2};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict
options:0 error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
// {"key1":1363395572.6129999,"key2":1363395572.613}
更新:正如在讨论中所说,问题在数据发生时已经发生
从服务器发送的JSON对象中读取。以下示例显示了这一点
NSJSONSerialization
无法读取超过的浮点数
来自JSON数据的“双精度”:
NSString *jsonString = @"{\"key1\":1363395572.6129999,\"key2\":1363395572.613}";
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *dict2 = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:NULL];
NSNumber *n1 = dict2[@"key1"];
NSNumber *n2 = dict2[@"key2"];
BOOL b = [n1 isEqualTo:n2]; // YES
答案 1 :(得分:3)
使用NSDecimalNumber
:
NSDecimalNumber* dc = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%.7f", myDouble]];
在字典中使用此十进制数字。
如果您精确地具有所需数字的字符串值,请将其直接提供给NSDecimalNumber
构造函数以获取精确的十进制数。不要使用中间double
阶段,否则会失去精确度。
答案 2 :(得分:1)
我刚试过,似乎精确度在序列化中丢失,而不是在数字的初始化中丢失。也许JSON序列化正在构建一个具有精确丢失数字格式的字符串,然后从中构建数据。
一种解决方案是将积分和小数值保存在自己的8字节字段中......也许使用两个LP64长。精度部分可以乘以大的东西,然后在检索时分开。
答案 3 :(得分:1)
我测试了以下代码:
double myDouble = 1363395572.6129999;
NSString *s = [NSString stringWithFormat:@"%.7f", myDouble];
NSLog(@"%@", s);
输出是:
1363395572.6129999
所以只需在放入JSON对象之前自己格式化字符串,你应该没问题。
编辑:
如果您想要更高的精度,而不仅仅是对JSON内容的更多控制,long double
将存储它。因此:
long double myDouble = 1363395572.6129999;
NSString *s = [NSString stringWithFormat:@"%.7L", myDouble];
NSLog(@"%@", s);