美文网首页iOS
金额相关精度问题

金额相关精度问题

作者: LittleSakana | 来源:发表于2016-12-23 15:59 被阅读131次

    最近项目中遇到金额计算,CGFloat类型金额计算的过程中,让我吃了不少苦头,先来看下面几行代码:

    示例代码

        CGFloat var1 = 120.66;
        CGFloat var2 = 120.66;
        
        CGFloat var3 = [@"120.66" floatValue];
        CGFloat var4 = [@"120.66" floatValue];
        
        CGFloat var5 = 120.67 - 0.01;
        
        CGFloat var6 = [@"120.67" floatValue] - 0.01;
        
        bool flag1 = var1 == var2;
        bool flag2 = var3 == var4;
        bool flag3 = var3 == var5;
        bool flag4 = var5 == var6;
        bool flag5 = var3 == var6;
    

    略去猜答案的过程,直接看结果

    图一图一

    现象总结

    • var1 和var2相等
    • var3 和var4相等
    • NSString 的 floatValue 会让CGFloat 的精度增加
    • 精度确定的float类型做运算,精度确定 var5 = 120.66
    • 不同精度的float类型做运算,精度会丢失 var6 = 120.65999816894531

    浮点数相等问题

        CGFloat x = var5 - var6;  
        const CGFloat EPSINON = 0.000001;  
        if (( x >= -EPSINON ) && ( x <= EPSINON ))  
        {  
            NSLog("var5 和var6相等");  
        } 
    

    鉴于CGFloat在处理金额过程中会遇到精度问题,iOS中提供了NSDecimalNumber类来处理精度问题,如果要四舍五入的话还要使用另一个类 NSDecimalNumberHandler

    NSDecimalNumber类

    /*
         讲述下参数的含义:
         RoundingMode: 简单讲就是你要四舍五入操作的标准.
    typedef NS_ENUM(NSUInteger, NSRoundingMode) {
        NSRoundPlain,   // 精度位后四舍五入
        NSRoundDown,    // 精度位后舍去
        NSRoundUp,      // 精度位后入上去
        NSRoundBankers  // 精度位后的数值等于5时,分两种情况:5后面还有其他数字(非0),则进位后舍去;若5后面是0(即5是最后一位),则根据5前一位数的奇偶性来判断是否需要进位,奇数进位,偶数舍去。
    };      
        // Rounding policies :
        // Original
        //    value 1.2  1.21  1.25  1.35  1.27
        // Plain    1.2  1.2   1.3   1.4   1.3
        // Down     1.2  1.2   1.2   1.3   1.2
        // Up       1.2  1.3   1.3   1.4   1.3
        // Bankers  1.2  1.2   1.2   1.4   1.3
    
         scale : 需要保留的精度。
         raiseOnExactness : 为YES时在处理精确时如果有错误,就会抛出异常。
         raiseOnOverflow  : YES时在计算精度向上溢出时会抛出异常,否则返回。
         raiseOnUnderflow : YES时在计算精度向下溢出时会抛出异常,否则返回.
         raiseOnDivideByZero : YES时。当除以0时会抛出异常,否则返回。
         */
         
         保留2位小数
    NSDecimalNumberHandler *roundUp = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain
                                                                                                scale:2
                                                                                     raiseOnExactness:NO
                                                                                      raiseOnOverflow:NO
                                                                                     raiseOnUnderflow:NO
                                                                                  raiseOnDivideByZero:YES];                                                                                                                                                 
    

    计算金额并四舍五入

    NSDecimalNumber *originMoney = [NSDecimalNumber decimalNumberWithString:num];//通过字符串计算金额
    NSDecimalNumber *roundMoney = [subtotal decimalNumberByRoundingAccordingToBehavior:roundUp];//按照设定的规则计算出金额
    

    金额计算实例

    NSDecimalNumber *varDecimal1 = [NSDecimalNumber decimalNumberWithString:@"120.661"];//120.661
    NSDecimalNumber *varDecimal2 = [NSDecimalNumber decimalNumberWithString:@"120.670"];//120.670
    
    //加
    [varDecimal1 decimalNumberByAdding:varDecimal2];
    
    //减
    NSDecimalNumber *varDecimal3 = [[varDecimal2 decimalNumberBySubtracting:varDecimal1] decimalNumberByRoundingAccordingToBehavior:roundUp];//0.01
    
    //乘
    [varDecimal1 decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithString:@"0.005"]];
    
    //除
    [varDecimal1 decimalNumberByDividingBy:varDecimal2];
    
    //比较
    [varDecimal1 compare:varDecimal2] == NSOrderedAscending //varDecimal1小于varDecimal2
    

    总结

    1. 金额相关,后台要传字符串到前台
    2. 计算过程中要用NSDecimalNumber来防止计算过程中出现精度错误
    3. [@"120.67" floatValue] - 0.01 不等于120.66

    相关文章

      网友评论

        本文标题:金额相关精度问题

        本文链接:https://www.haomeiwen.com/subject/nqvkvttx.html