在 iOS 中经常会计算金额和价格,我们有时会定义数据类型为 double 或者 float,这样在做过一些运算后会发现精度丢失了,这显然不是我们想要的结果。今日偶然间看到一篇技术博文,为了记忆,顺道解决我的这个问题,所以记录了下来。
存在的问题(精度丢失)
float a = 0.01;
int b = 99999999;
double c = 0.0;
c = a*b;
NSLog(@"c-%f",c); // c-1000000.000000
NSLog(@"c-%.2f",c); // c-1000000.00
通过上面的代码我们看到简单的数学运算后对于数据类型不合理的数值进行过运算后精度丢失了,这如果是在我们的 App 中,用户看到自己的金额不正确,那还不吓一跳??
接下来看看如何做简单的改进
NSString *aString = [NSString stringWithFormat:@"%f",a];
NSString *bString = [NSString stringWithFormat:@"%.2f",(double)b];
c = [aString doubleValue]*[bString doubleValue];
NSLog(@"%f",c); //999999.990000
NSLog(@"%.2f",c); //999999.99
这样虽然可以达成目的,但是计算的过程比较麻烦,并不是我们想要的解决方案。通过查阅资料得知苹果推出了一个类,专门解决数据计算的精度问题NSDecimalNumber 。
NSDecimalNumber 为数据精度应用而生
NSDecimalNumber 是 NSNumber 的子类,专门负责精度计算。提供了完善的初始化方案,对于头疼的精度计算问题(金额)它提供了便利的解决方案(加、减、乘、除、次方运算并且可以给计算出的结果设置明显的精度方案(四舍五入、取上、取下等等))。NSDecimalNumberHandler 可以对计算出的结果做一些策略,比如舍入的模式、数据溢出、除0等异常情况的处理规则。
我们来说说上面的问题吧,引入了 NSDecimalNumber,解决上面的问题就不费吹灰之力了。
NSString *decimalNumberMutiplyWithString(NSString *multiplierValue, NSString *multiplicandvalue){
NSDecimalNumber *multiplierNumber = [NSDecimalNumber decimalNumberWithString:multiplierValue];
NSDecimalNumber *multiplicandNumber = [NSDecimalNumber decimalNumberWithString:multiplicandvalue];
NSDecimalNumber *result = [multiplierNumber decimalNumberByMultiplyingBy:multiplicandNumber];
return [result stringValue];
}
NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithMantissa:18992 exponent:-3 isNegative:NO];
NSDecimalNumber *price2 = [NSDecimalNumber decimalNumberWithString:@"18.992"];
NSLog(@"price:%@",[price stringValue]); //999999.990000
NSLog(@"price2:%@",[price2 stringValue]); //999999.99
//设置计算的精度(小数点位数、舍入规则)
NSDecimalNumberHandler *roundPlain = [NSDecimalNumberHandler
decimalNumberHandlerWithRoundingMode:NSRoundPlain
scale:1
raiseOnExactness:NO
raiseOnOverflow:NO
raiseOnUnderflow:NO
raiseOnDivideByZero:YES];
NSDecimalNumber *resultDecimal = [multiplierNumber decimalNumberByMultiplyingBy:multiplicandNumber withBehavior:roundPlain];
参考文章
文章1、文章2