在 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