• iOS 金融类高精度处理


    金融类app各种数值的精确计算问题 

    1.简单粗暴的方法—doubleValue解决

        刚开始接手的项目中关于数值的计算就是按照字符串直接转doubleValue的方式转化为后进行加减乘除运算。这样做在大多数情况下是不会有问题的,但如果经过全面测试的话,
    有些字符串转double会出现精度损失问题,计算结果也会出现不准确的问题,一个最近踩过的坑就是用户再输入投资金额的时候会对自己账户中的可用余额进行判断,以确定余额是否
    充足,测试的时候输入很多数据都没问题,但线上用户大量数据就会出现偶尔数据错误,用户输入金额明明和可用余额一样,但判断的时候就会提示可用余额不足,老板使用过程中也遇
    到过,还找了我好几次,汗,都是之前开发人员的坑。所以记住这丫简单粗暴的方法针对金融类的对数据精度及其敏感的项目不可取。

       例如:字符串@“0.985” 转 doubleValue 后 变成了 0.894999999 等

    2.明智之举—NSDecimalNumber

    NSDecimalNumber是苹果提供的专门金融货币精确数值计算的API。

    + (instancetype)decimalNumberHandlerWithRoundingMode:(NSRoundingMode)roundingMode
                                                   scale:(short)scale
                                        raiseOnExactness:(BOOL)raiseOnExactness
                                         raiseOnOverflow:(BOOL)raiseOnOverflow
                                        raiseOnUnderflow:(BOOL)raiseOnUnderflow
                                     raiseOnDivideByZero:(BOOL)raiseOnDivideByZero 
    参数说明
    roundingMode 要使用的舍入模式,有四种值: NSRoundUp, NSRoundDown, NSRoundPlain, and NSRoundBankers
    scale 结果保留几位小数
    raiseOnExactness 发生精确错误时是否抛出异常,一般为NO
    raiseOnOverflow 发生溢出错误时是否抛出异常,一般为NO
    raiseOnUnderflow 发生不足错误时是否抛出异常,一般为NO
    raiseOnDivideByZero 被0除时是否抛出异常,一般为YES
    2.1 NSDecimalNumber 的使用。

    NSDecimalNumber是进行数值计算的对象,ji所有的加减乘除,幂运算操作对象。

    NSDecimalNumberHandler *handler = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundBankers
                                                                                                 scale:2
                                                                                      raiseOnExactness:NO
                                                                                       raiseOnOverflow:NO
                                                                                      raiseOnUnderflow:NO
                                                                                   raiseOnDivideByZero:YES];
        NSDecimalNumber *num1           = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%@",@"2.37"]];
        NSDecimalNumber *num2           = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%@",@"15.89"]];
    
        //加
        NSDecimalNumber *addResult      = [num1 decimalNumberByAdding:num2
                                                         withBehavior:handler];
        //减
        NSDecimalNumber *subtractResult = [num1 decimalNumberBySubtracting:num2
                                                              withBehavior:handler];
        //乘
        NSDecimalNumber *multiplyResult = [num1 decimalNumberByMultiplyingBy:num2
                                                                withBehavior:handler];
        //除
        NSDecimalNumber *divideResult   = [num1 decimalNumberByDividingBy:num2
                                                           withBehavior:handler];
        //平方
        NSDecimalNumber *powerResult    = [num1 decimalNumberByRaisingToPower:2
                                                              withBehavior:handler];
        NSLog(@"
    %f
    %f
    %f
    %f
    %f",[addResult doubleValue],[subtractResult doubleValue],[multiplyResult doubleValue],[divideResult doubleValue],[powerResult doubleValue]);
    

      

    这样就能够很好地解决金融类app货币单位精确计算的问题。但有一点需要注意的是如果计算公式比较长,那么withBehavior应该在最后一步使用,切不可在计算过程中进行舍入操作,相信有点数学常识的同学都能明白这个问题。 
    如下:等额本息的预计收益计算 
    * 公式: 每月本息还款额=(投资金额月利率(1+月利率)^还款月数)/(((1+月利率)^还款月数)-1)

    /**
     *  等额本息标的收益计算
     *  公式: 每月本息还款额=(投资金额*月利率*(1+月利率)^还款月数)/(((1+月利率)^还款月数)-1)
     *  @param interest     年化收益
     *  @param investAmount 投资金额
     *  @param selectBid    选择的标
     *
     *  @return 收益
     */
    + (double)calculatePrincipalAndInterestBidWithInvestInterest:(double)interest
                                                          amount:(NSString *)investAmount
                                                             bid:(Bid *)selectBid{
    
        NSDecimalNumberHandler *roundBanker = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundBankers
                                                                                                     scale:2.0f
                                                                                          raiseOnExactness:NO
                                                                                           raiseOnOverflow:NO
                                                                                          raiseOnUnderflow:NO
                                                                                       raiseOnDivideByZero:YES];
        NSDecimalNumber *investAmountDec = [[NSDecimalNumber alloc] initWithString:investAmount];//投资金额
        NSDecimalNumber *monthInterestDec = [[NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%f",interest/100]] decimalNumberByDividingBy:[NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%f",12.00]]];//月利率
    
        double month = selectBid.creditPeriod;//标的募集期数
        if (selectBid.creditType == CreditType_DaylyOneOffPayment) {
            //按天标,转化为月份
            month = month/30.00;
        }
        NSDecimalNumber *monthDec = [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%f",month]];//还款月数
    //    *  公式: 每月本息还款额=(投资金额*月利率*(1+月利率)^还款月数)/(((1+月利率)^还款月数)-1)
        NSDecimalNumber *decNum1 = [[[[[NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%d",1]] decimalNumberByAdding:monthInterestDec] decimalNumberByRaisingToPower:month] decimalNumberByMultiplyingBy:monthInterestDec] decimalNumberByMultiplyingBy:investAmountDec];
        NSDecimalNumber *decNum2 = [[[[NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%d",1]] decimalNumberByAdding:monthInterestDec] decimalNumberByRaisingToPower:month] decimalNumberBySubtracting:[NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:@"%d",1]]];
        NSDecimalNumber *monthIncomeDec = [decNum1 decimalNumberByDividingBy:decNum2];//月本息还款额
        NSDecimalNumber *totalIncomeDec = [[monthDec decimalNumberByMultiplyingBy:monthIncomeDec] decimalNumberBySubtracting:investAmountDec withBehavior:roundBanker];//总还款额 - 本金 = 总收益
        return [totalIncomeDec doubleValue] > 0 ? [totalIncomeDec doubleValue] : 0;
    }
    

      

    参考来自:http://blog.csdn.net/oximing1/article/details/50456969

  • 相关阅读:
    开发笔记-图片拉伸保持不变形
    开发笔记-简单渐变动画的实现
    开发笔记- iOS监听某些事件的方法简单梳理
    开发笔记-UIApplication代理
    开发笔记-UIApplication单例
    常见的UNIX命令
    知识点回顾-简单的TableView单组数据展示/多组数据展示
    开发技巧-改变按钮属性3部曲
    字典--plist
    如何使用JS实现页面内容随机显示
  • 原文地址:https://www.cnblogs.com/saytome/p/7371529.html
Copyright © 2020-2023  润新知