Files
knowledge-kit/Chapter1 - iOS/1.36.md
2020-02-25 17:46:51 +08:00

73 lines
3.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# iOS 数值计算精度丢失问题
> 在 iOS 中经常会计算金额和价格,我们有时会定义数据类型为 double 或者 float这样在做过一些运算后会发现精度丢失了这显然不是我们想要的结果。今日偶然间看到一篇技术博文为了记忆顺道解决我的这个问题所以记录了下来。
### 存在的问题(精度丢失)
```objective-c
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 中,用户看到自己的金额不正确,那还不吓一跳??
接下来看看如何做简单的改进
```objective-c
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解决上面的问题就不费吹灰之力了。
```objective-c
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](https://www.jianshu.com/p/25d24a184016)、[文章2](https://www.jianshu.com/p/ea4da259a062)