Objective-C中浮点数表示时精度的小坑

本文迁移自本人简书帐号酱油葱, 后续不会再在简书更新文章, 具体缘由能够查看简书CEO盛赞程序员出轨率高“真实、新鲜、多元” ,对不起打扰了程序员

发现问题

近日测试的童鞋告诉我, 你家APP的四舍五入有毛病. 明明是1.895 => 1.90, 为什么出来的结果是1.89. 打死不信, 明明用的是四舍五入, 为什么Format出来结果竟是如此. 因而花了小半天的时间, 才发现问题症结所在 -- [NSString stringWithFormat:]bash

缘由

关于Format为何不能做为四舍五入处理办法的描述框架

You should only ever do this while formatting a number for display to the end user, because there is no guarantee that.post

类同于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, 结果可想而知.ui

解决思路

浮点数据处理的正确作法参见Wiki 既然浮点型数据的存储和计算自有其规则, 天然应该按照规则来办事, 才能得出正确的结果. 做为成熟的Cocoa框架, 必然有针对性的API作浮点型数据的Format处理. 如下是改造的内容:this

@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

复制代码

结果

测试用例以下:spa

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]);
复制代码

测试结果以下:3d

距离为:2
距离为:1.89
距离为:1.90
距离为:1.90
距离为:1.90
复制代码

结论

数据处理在不要求精度的APP中能够不用作什么理会, 可是若是考虑到数据不一样精度的表达和计算时, 必定要充分考虑各类因素, 谨慎处理. 以避免犯下阿波罗号的惨剧code

鸣谢

感谢本司的测试童鞋和开发童鞋的支持, 发现并及时填上了这个坑.

相关文章
相关标签/搜索