Ios NSNumber双精度数字,小数位数被舍入/截断
我有一个Ios NSNumber双精度数字,小数位数被舍入/截断,ios,objective-c,nsnumber,Ios,Objective C,Nsnumber,我有一个NSNumber中的double double myDouble = 1363395572.6129999; NSNumber *doubleNumber = @(myDouble); // using [NSNumber numberWithDouble:myDouble] leads to the same result 这就是问题所在 doubleNumber.doubleValue似乎返回正确的完整值(1363395572.6129999) 但是,在调试器中查看double
NSNumber
中的double
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
。我的问题的一个更好的解决方案是我在上一段中概述的,因此我没有采用这种方法。我只是尝试了一下,但似乎精度在序列化过程中丢失了,而不是在数字初始化过程中。也许JSON序列化是构建一个精确的字符串,丢失数字格式,然后从中构建数据
一种解决方案是将整数值和分数值保存在它们自己的8字节字段中。。。可能使用两个LP64长。精度部分可以乘以较大的值,然后在检索时进行除法。我测试了以下代码:
double myDouble = 1363395572.6129999;
NSString *s = [NSString stringWithFormat:@"%.7f", myDouble];
NSLog(@"%@", s);
输出为:
1363395572.6129999
所以,在放入JSON对象之前,只需自己格式化字符串,就可以了
编辑:
如果您想要更高的精度,而不仅仅是更多地控制JSON中的内容,那么长双精度
将存储它。因此:
long double myDouble = 1363395572.6129999;
NSString *s = [NSString stringWithFormat:@"%.7L", myDouble];
NSLog(@"%@", s);
如上所述,
double
的精度约为16位小数
数字<代码>1363395572.612999有17位数字,并转换此十进制数
todouble
给出的结果与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
的示例:
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}
NSDecimalNumber* dc = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%.7f", myDouble]];
更新:正如在讨论中所发现的,当数据
从服务器发送的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
使用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}
NSDecimalNumber* dc = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%.7f", myDouble]];
在字典里使用这个十进制数字
如果您有一个精确的所需数字的字符串值,请直接将其输入到NSDecimalNumber
构造函数以获得精确的十进制数。不要使用中间double
阶段,这样会降低精度。1363395572.612999IS是1363395572.613Adouble
只能存储大约13个有效数字。description
仅用于调试,不应使用(少数对象类型除外)要准确地反映对象的内容,需要做的一件事是制作一个[NSString stringWithFormat:@“%.7f”,myDouble],看看它是什么样子。如果您可以在将其放入JSON之前强制执行您想要的内容,那么应该可以解决问题。{EDIT:posted as answer,tested and works]@RobP-除了在JSON中输入字符串而不是数字。对于double myDouble=1363395572.613
您会得到相同的输出1363395572.6129999
。因此这不会“增加”精度。是的,这一点很好,但至少在这一点上,您不会受到NSJSONSerialization功能的神秘影响。我只是验证了,即使使用typelong double
(至少在64位体系结构上)这1363395572.613==1363395572.6129999
的计算结果为真。所以这只是一个“你希望它以什么格式出现在游戏中”的问题,因为基本值是相同的……我刚刚投了赞成票。如果你对你想要的JSON有具体的想法,我仍然认为在一个代码中格式化字符串不是一个坏主意。但是如果这个问题是否为基础精度?然后是的,需要更改值的存储类型!请注意,这是一个NSString,而不是NSNumber。这是一个显著的差异。确实存在差异,但取决于JSON的位置,它不一定是坏的。是的。如果目标是发送存储在double myDouble
中的值,则结果t“1363395572.613”是“正确的”。如果目标是发送号码“1363395572.6129999”,则不能先将其存储在double
中。@Dima”JSON num