采取以下代码:
NSError *error;
NSString *myJSONString = @"{ \"foo\" : 0.1}";
NSData *jsonData = [myJSONString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *results = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&error];
我的问题是,results[@"foo"]
是一个NSDecimalNumber,还是具有有限二进制精度的东西,如double或float?基本上,我有一个应用程序需要NSDecimalNumber
带来的无损准确性,并且需要确保JSON反序列化不会因为双精度/浮点数等而导致舍入。
E.g。如果它被解释为浮点数,我会遇到精确的问题:
float baz = 0.1;
NSLog(@"baz: %.20f", baz);
// prints baz: 0.10000000149011611938
我尝试将foo
解释为NSDecimalNumber并打印结果:
NSDecimalNumber *fooAsDecimal = results[@"foo"];
NSLog(@"fooAsDecimal: %@", [fooAsDecimal stringValue]);
// prints fooAsDecimal: 0.1
但后来我发现在stringValue
上调用NSDecimalNumber
并不打印所有有效数字,例如......
NSDecimalNumber *barDecimal = [NSDecimalNumber decimalNumberWithString:@"0.1000000000000000000000000000000000000000000011"];
NSLog(@"barDecimal: %@", barDecimal);
// prints barDecimal: 0.1
...所以打印fooAsDecimal
并没有告诉我JSON解析器是否在某些时候将results[@"foo"]
舍入到有限精度。
要清楚,我意识到我可以使用字符串而不是JSON表示中的数字来存储foo的值,即"0.1"
而不是0.1
,然后使用{{1} }]。但是,我感兴趣的是NSJSONSerialization类如何反序列化JSON数字,所以我知道这是否真的有必要。
答案 0 :(得分:6)
NSJSONSerialization
(以及Swift中的JSONSerialization
)遵循一般模式:
long long
。如果没有溢出,请使用NSNumber
返回long long
。strtod_l
解析双精度数。如果它没有溢出,请使用NSNumber
返回double
。NSDecimalNumber
,特别是最多38位的尾数和-128 ... 127之间的指数。如果您查看人们发布的其他示例,您可以看到当值超出double
的范围或精度时,您会得到NSDecimalNumber
。
答案 1 :(得分:4)
简短的回答是,如果需要NSDecimalNumber级别的精度,则不应序列化为JSON。 JSON只有一种数字格式:double,它的精度低于NSDecimalNumber。
答案很长,只有学术兴趣,因为简短的答案也是正确的答案,是“不一定”。 NSJSONSerialization有时会反序列化为NSDecimalNumber,但它没有记录,我还没有确定,它所处理的环境是什么。例如:
BOOL boolYes = YES;
int16_t int16 = 12345;
int32_t int32 = 2134567890;
uint32_t uint32 = 3124141341;
unsigned long long ull = 312414134131241413ull;
double dlrep = 1.5;
double dlmayrep = 1.1234567891011127;
float fl = 3124134134678.13;
double dl = 13421331.72348729 * 1000000000000000000000000000000000000000000000000000.0;
long long negLong = -632414314135135234;
unsigned long long unrepresentable = 10765432100123456789ull;
dict[@"bool"] = @(boolYes);
dict[@"int16"] = @(int16);
dict[@"int32"] = @(int32);
dict[@"dlrep"] = @(dlrep);
dict[@"dlmayrep"] = @(dlmayrep);
dict[@"fl"] = @(fl);
dict[@"dl"] = @(dl);
dict[@"uint32"] = @(uint32);
dict[@"ull"] = @(ull);
dict[@"negLong"] = @(negLong);
dict[@"unrepresentable"] = @(unrepresentable);
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:NSJSONWritingPrettyPrinted error:nil];
NSDictionary *dict_back = (NSDictionary *)[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
并在调试器中:
(lldb) po [dict_back[@"bool"] class]
__NSCFBoolean
(lldb) po [dict_back[@"int16"] class]
__NSCFNumber
(lldb) po [dict_back[@"int32"] class]
__NSCFNumber
(lldb) po [dict_back[@"ull"] class]
__NSCFNumber
(lldb) po [dict_back[@"fl"] class]
NSDecimalNumber
(lldb) po [dict_back[@"dl"] class]
NSDecimalNumber
(lldb) po [dict_back[@"dlrep"] class]
__NSCFNumber
(lldb) po [dict_back[@"dlmayrep"] class]
__NSCFNumber
(lldb) po [dict_back[@"negLong"] class]
__NSCFNumber
(lldb) po [dict_back[@"unrepresentable"] class]
NSDecimalNumber
所以你要做的就是这样。你绝对不应该假设如果你将NSDecimalNumber序列化为JSON,你将得到一个NSDecimalNumber。
但是,再次,您不应该将NSDecimalNumbers存储在JSON中。
答案 2 :(得分:1)
要回答标题中的问题:不,它不会,它会创建NSNumber
个对象。你可以轻松地测试一下:
NSArray *a = @[[NSDecimalNumber decimalNumberWithString:@"0.1"]];
NSData *data = [NSJSONSerialization dataWithJSONObject:a options:0 error:NULL];
a = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
NSLog(@"%@", [a[0] class]);
将打印__NSCFNumber
。
您可以使用NSNumber
将NSDecimalNumber
个对象转换为[NSDecimalNumber decimalNumberWithDecimal:[number decimalValue]]
,但根据decimalValue
对于float和double值,返回的值不保证是精确的。
答案 3 :(得分:1)
我遇到了同样的问题,除了我使用Swift 3.我做了a patched version of the JSONSerialization class,将所有数字解析为Decimal
' s。它只能解析/反序列化JSON,但没有任何序列化代码。它基于Apple在Swift中重新实现Foundation的开源。