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

鳴謝

感謝本司的測試童鞋和開發童鞋的支持, 發現並及時填上了這個坑.

相關文章
相關標籤/搜索