float和 double精度问题

因为是做金融类APP的,所以项目中也就不得不接触到了金额这一概念,但是就是这么个常见的场景,一个不留神,就让我摔了一个跟头.出现的问题如标题所言,是因为数据的精度导致的.

float类型的最大容量是8位,而double类型的容量为16位.在项目中我疏忽大意了,在进行字符型向浮点型转换的时候,用的不是double,而是用的float.也就是说当我的数据如果整数和小数的有效数字部分没有超过8位,那么结果是正常的,但是当我的数据超过8位的时候,数据就出现问题了.

看下面的例子

NSString *temstr=@"12345678.90";
NSLog(@"数据类型%.2f,%.2f",[temstr floatValue],[temstr doubleValue]);

输出为:数据类型12345679.00,12345678.90

所以在项目开发过程中字符串和浮点类型的转换最好用double类型。但是double类型如果超出16位也会失真,但大部分情况下已经够用了。

其实还有更规范的方式去解决上述问题,接下来看一下NSDecimalNumber

NSDecimalNumber简介:这是一个十进制数字类,继承自NSNumber,苹果针对浮点类型计算精度问题提供出来的计算类,基于十进制的科学计数法来计算,同时可以指定舍入模式,一般用于货币计算。

一、科学计数法

//15.99 用十进制科学计数法可以表达为 +1599 × 10⁻²,看下面用代码怎么表示
    NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithMantissa:1599 exponent:-2 isNegative:NO];
    NSLog(@"科学计数法表示 %@",price);//科学计数法表示 15.99

二、基本的加减乘除用法

NSDecimalNumber *price1 = [NSDecimalNumber decimalNumberWithString:@"15.99"];
    NSDecimalNumber *price2 = [NSDecimalNumber decimalNumberWithString:@"29.99"];
    NSDecimalNumber *coupon = [NSDecimalNumber decimalNumberWithString:@"5.00"];
    NSDecimalNumber *discount = [NSDecimalNumber decimalNumberWithString:@".90"];
    NSDecimalNumber *numProducts = [NSDecimalNumber decimalNumberWithString:@"2.0"];
    //加
    NSDecimalNumber *subtotal = [price1 decimalNumberByAdding:price2];
    //减
    NSDecimalNumber *afterCoupon = [subtotal decimalNumberBySubtracting:coupon];
    //乘
    NSDecimalNumber *afterDiscount = [afterCoupon decimalNumberByMultiplyingBy:discount];
    //除
    NSDecimalNumber *average = [afterDiscount decimalNumberByDividingBy:numProducts];
    //次方
    NSDecimalNumber *averageSquared = [average decimalNumberByRaisingToPower:2];
    //10为底的乘方运算
    NSDecimalNumber *powerNum = [numProducts decimalNumberByMultiplyingByPowerOf10:2];

    NSLog(@"Subtotal: %@", subtotal);                    // 45.98
    NSLog(@"After coupon: %@", afterCoupon);             // 40.98
    NSLog((@"After discount: %@"), afterDiscount);       // 36.882
    NSLog(@"Average price per product: %@", average);    // 18.441
    NSLog(@"Average price squared: %@", averageSquared); // 340.070481
    NSLog(@"--powerNum: %@", powerNum);                  // 200

三、NSDecimalNumberHandler

这是一个NSDecimalNumber的公共协议处理类,可以设置舍入模式以及计算错误的处理;配合NSDecimalNumber来使用,将这个类的实例当做NSDecimalNumber相应API的参数来控制数字处理的结果。
//取整方式
    /*
     // 要处理的value     1.2  1.21  1.25  1.35  1.27
     // NSRoundPlain    1.2  1.2   1.3   1.4   1.3  四舍五入
     // NSRoundDown     1.2  1.2   1.2   1.3   1.2  只舍不入
     // NSRoundUp       1.2  1.3   1.3   1.4   1.3  只入不舍
     // NSRoundBankers  1.2  1.2   1.2   1.4   1.3  (在四舍五入的基础上加了一个判断:当最后一位为5的时候,只会舍入成偶数。比如:1.25不会返回1.3而是1.2,因为1.3不是偶数。)
     */
//scale:保留有效小数的个数(为0的无效小数后自动过滤).
    //Exactness:进度异常、Overflow:向上溢出、Underflow:向下溢出、DivideByZero:除数为0。当参数为YES出错会抛出异常,为NO时忽略异常。返回nil.

NSDecimalNumberHandler *roundUp = [NSDecimalNumberHandler
            decimalNumberHandlerWithRoundingMode:NSRoundUp
                                       scale:0
                                       raiseOnExactness:NO
                                       raiseOnOverflow:NO
                                       raiseOnUnderflow:NO
                                       raiseOnDivideByZero:YES];
[average decimalNumberByRaisingToPower:2 withBehavior:roundUp];
    
    
    //舍入运算
    NSDecimalNumber *includedNum = [price1 decimalNumberByRoundingAccordingToBehavior:roundUp];
    NSLog(@"--powerNum: %@", includedNum);          // 16

PS:NSDecimalNumber同时提供了isEqualToNumber:方法和NSNumber进行判断是否相等。

你可能感兴趣的:(float和 double精度问题)