本文迁移自本人简书账号, 后续不会再在简书更新文章, 具体原因可以查看
发现问题
近日测试的童鞋告诉我, 你家APP的四舍五入有毛病. 明明是1.895 => 1.90, 为何出来的结果是1.89. 打死不信, 明明用的是四舍五入, 为何Format出来结果竟是如此. 于是花了小半天的时间, 才发现问题症结所在 -- [NSString stringWithFormat:]
原因
You should only ever do this while formatting a number for display to the end user, because there is no guarantee that.
类同于printf
不保证实现四舍五入, [NSString stringWithFormat:]
方法也不能保证实现四舍五入, 作为数字描述信息的展示, 永远只能是近似, 更何况浮点型数据在计算机存储的精度是不准确且有限的.
/******************************************************************************** Base floating point types Float32 32 bit IEEE float: 1 sign bit, 8 exponent bits, 23 fraction bits Float64 64 bit IEEE float: 1 sign bit, 11 exponent bits, 52 fraction bits Float80 80 bit MacOS float: 1 sign bit, 15 exponent bits, 1 integer bit, 63 fraction bits Float96 96 bit 68881 float: 1 sign bit, 15 exponent bits, 16 pad bits, 1 integer bit, 63 fraction bits Note: These are fixed size floating point types, useful when writing a floating point value to disk. If your compiler does not support a particular size float, a struct is used instead. Use of of the NCEG types (e.g. double_t) or an ANSI C type (e.g. double) if you want a floating point representation that is natural for any given compiler, but might be a different size on different compilers.*********************************************************************************/复制代码
Cocoa框架针对浮点基本类型也有详细的描述, 超过7位(包括小数点整数部分)的Float32数精度本身已不可靠, 如果再依赖此数据进行format
, 结果可想而知.
解决思路
既然浮点型数据的存储和计算自有其规则, 自然应该按照规则来办事, 才能得出正确的结果. 作为成熟的Cocoa框架, 必然有针对性的API做浮点型数据的Format处理. 以下是改造的内容:
@implementation ODMNumberFormatter+ (ODMNumberFormatter *)shareInstance { static ODMNumberFormatter *instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[ODMNumberFormatter alloc] init]; instance.numberFormatter = [[NSNumberFormatter alloc] init]; instance.numberFormatter.numberStyle = NSNumberFormatterDecimalStyle; instance.numberFormatter.roundingMode = kCFNumberFormatterRoundHalfUp; }); return instance;}+ (NSString *)roundDecimalWithFormat:(NSString *)format value:(double)value { NSNumberFormatter *numberFormatter = [self shareInstance].numberFormatter; numberFormatter.positiveFormat = format; return [numberFormatter stringFromNumber:[NSNumber numberWithDouble:value]];}@end复制代码
结果
测试用例如下:
NSLog(@"距离为:%@", [ODMNumberFormatter roundDecimalWithFormat:@"0.##" value:1.995]);NSLog(@"距离为:%@", [ODMNumberFormatter roundDecimalWithFormat:@"0.00" value:1.894]);NSLog(@"距离为:%@", [ODMNumberFormatter roundDecimalWithFormat:@"0.00" value:1.895]);NSLog(@"距离为:%@", [ODMNumberFormatter roundDecimalWithFormat:@"0.00" value:1.899]);NSLog(@"距离为:%@", [ODMNumberFormatter roundDecimalWithFormat:@"0.00" value:1.900]);复制代码
测试结果如下:
距离为:2距离为:1.89距离为:1.90距离为:1.90距离为:1.90复制代码
结论
数据处理在不要求精度的APP中可以不用做什么理会, 但是如果考虑到数据不同精度的表达和计算时, 一定要充分考虑各种因素, 谨慎处理. 以免犯下
鸣谢
感谢本司的测试童鞋和的支持, 发现并及时填上了这个坑.