因为是做金融类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进行判断是否相等。
网友评论