在iOS開發中,和貨币價格計算相關的,需要注意計算精度的問題。即使隻是兩位小數,也會出現誤差。使用float類型運算,是完全不夠的。經過一番測試,最後選擇使用系統提供的API的NSDecimalNumber來進行更好的解決。
作為一個對外的庫,鑒于版本延續,我們保留對外的flaot的類型,不改變接口,選擇進行内部适配。
以下是一些基本的測試,
原始資料
float a = 0.01;
int b = 99999999;
double c = 0.0;
1:使用浮點運算,
c = a*b;
NSLog(@"%f",c);
NSLog(@"%.2f",c);
使用double類型存儲沒有觸及問題的實質,完全不能解決。
<b>2011-12-30 11:04:00.121 Untitled[2912:207] 1000000.000000</b>
<b></b>
<b>2011-12-30 11:04:00.123 Untitled[2912:207] 1000000.00</b>
2:使用類型轉換,提高精度
c = a*(double)b;
Double運算的精度是提高了,可是浮點數的數值早已經出現了精度的不準确,即使存儲空間足夠,同樣還是不準确的數值。
<b>2011-12-30 11:04:00.123 Untitled[2912:207] 999999.967648</b>
<b>2011-12-30 11:04:00.124 Untitled[2912:207] 999999.97</b>
3:通過和NSString的轉換,将計算的原始資料轉換為純粹的double類型的資料,這樣的計算精度就可以達到要求了。
NSString *objA = [NSString stringWithFormat:@"%.2f", a];
NSString *objB = [NSString stringWithFormat:@"%.2f", (double)b];
c = [objA doubleValue] * [objB doubleValue];
計算的結果還是比較準确的,不過需要做格式化輸入和格式化輸出的處理。同時使用NSString來轉換,這樣的寫法看起來比較奇怪。
<b>2011-12-30 11:04:00.190 Untitled[2912:207] 999999.99</b>
4:個人還是比較喜歡使用系統提供的類型來進行計算。通過NSDecimalNumber提供的計算方式,可以很好的計算出準确的精度的資料,同時不需要使用格式化輸出等。
其計算的精度是比較高,這是官方建議的貨币計算的API,對乘除等計算都有單獨的API接口來提供。
NSString *decimalNumberMutiplyWithString(NSString *multiplierValue,NSString *multiplicandValue)
{
NSDecimalNumber *multiplierNumber = [NSDecimalNumber decimalNumberWithString:multiplierValue];
NSDecimalNumber *multiplicandNumber = [NSDecimalNumber decimalNumberWithString:multiplicandValue];
NSDecimalNumber *product = [multiplicandNumber decimalNumberByMultiplyingBy:multiplierNumber];
return [product stringValue];
}
NSLog(@"%@",decimalNumberMutiplyWithString([NSString stringWithFormat:@"%f",a], [NSString stringWithFormat:@"%d",b]));
隻是測試,是以接口名大緻寫寫,名字取得比較不那麼講究,希望可以表達清楚。
總的來說,對于貨币計算,應該需要注意精度的問題。同時在運算的時候,應該優先選用架構提供的API,否則,就應該使用足夠精度的類型運算,同時對自己寫的接口進行足夠的說明,要求開發者按照規範來使用。
在自己不能保證足夠準确的情況下,用适當的說明的要求來規避責任還是可以接受的。至少被人抱怨兩句總比出錯強。
本文轉自 arthurchen 51CTO部落格,原文連結:http://blog.51cto.com/arthurchen/761426,如需轉載請自行聯系原作者