floata =0.01;intb =99999999;doublec =0.0;c = a*b;NSLog(@"%f",c);//輸出結(jié)果為 1000000.000000NSLog(@"%.2f",c);//輸出結(jié)果為 1000000.00//明顯不夠精確
在網(wǎng)上找到了一個國內(nèi)朋友的博客也遇到和我一樣的問題,他嘗試了如下兩種解決方案
將float強(qiáng)制轉(zhuǎn)換為double
c= a*(double)b;NSLog(@"%f",c);//輸出結(jié)果? 999999.967648NSLog(@"%.2f",c);//輸出結(jié)果? 999999.97// 明顯已經(jīng)丟失精度
通過和NSString的轉(zhuǎn)換,將計算的原始數(shù)據(jù)轉(zhuǎn)換為純粹的double類型的數(shù)據(jù),這樣的計算精度就可以達(dá)到要求了
NSString*objA = [NSStringstringWithFormat:@"%.2f", a];NSString*objB = [NSStringstringWithFormat:@"%.2f", (double)b];c = [objA doubleValue] * [objB doubleValue];NSLog(@"%.2f",c);//輸出結(jié)果? 999999.99
最終方案
NSString*decimalNumberMutiplyWithString(NSString*multiplierValue,NSString*multiplicandValue){NSDecimalNumber*multiplierNumber = [NSDecimalNumberdecimalNumberWithString:multiplierValue];NSDecimalNumber*multiplicandNumber = [NSDecimalNumberdecimalNumberWithString:multiplicandValue];NSDecimalNumber*product = [multiplicandNumber decimalNumberByMultiplyingBy:multiplierNumber];return[product stringValue];}NSLog(@"%@",decimalNumberMutiplyWithString([NSStringstringWithFormat:@"%f",a], [NSStringstringWithFormat:@"%d",b]));//輸出結(jié)果? 999999.99
下面開始講解這個NSDecimalNumber
The NSDecimalNumber class provides fixed-point arithmetic算法capabilities功能to Objective-C programs. They’re designed to perform base-10 calculations without loss of precision精度and with predictable可預(yù)測的rounding湊整behavior. This makes it a better choice for representing表示currency貨幣than floating-point data types like double. However, the trade-off is that they are more complicated to work with.
NSDecimalNumber這個類為OC程序提供了定點(diǎn)算法功能,它被設(shè)計為了不會損失精度并且可預(yù)先設(shè)置湊整規(guī)則的10進(jìn)制計算,這讓它成為一個比浮點(diǎn)數(shù)(double)更好的選則去表示貨幣,然而作為交換用NSDecimalNumber計算變得更加復(fù)雜

Internally, a fixed-point number is expressed as表示為sign符號mantissa尾數(shù)x 10^exponent指數(shù). The sign defines whether it’s positive or negative, the mantissa is an unsigned integer representing the significant有意義的digits有效數(shù)字, and the exponent determines where the decimal小數(shù)point falls in the mantissa.
在內(nèi)部,一個有小數(shù)點(diǎn)的數(shù)被表示為上圖中的這種形式,這個符號定義了它是正數(shù)還是負(fù)數(shù),這個尾數(shù)是一個無符號的整數(shù)用來表示有效數(shù)字,這個指數(shù)決定了小數(shù)點(diǎn)在尾數(shù)中的位置
It’s possible to對...是可能的manually手動地assemble裝配an NSDecimalNumber from a mantissa, exponent, and sign, but it’s often easier to convert it from a string representation表示. The following snippet片段creates the value 15.99 using both methods.
NSDecimalNumber*price;? ? price = [NSDecimalNumberdecimalNumberWithMantissa:1599exponent:-2isNegative:NO];? ? price = [NSDecimalNumberdecimalNumberWithString:@"15.99"];
對手動地用尾數(shù),指數(shù),符號來裝配一個NSDecimalNumber是可能的,但是但是從一個字符串表示轉(zhuǎn)換成一個NSDecimalNumber更容易,以下的片段創(chuàng)建了值15.99用兩個方法
Like NSNumber, all NSDecimalNumber objects are immutable不可變的, which means you cannot change their value after they’ve been created.
像NSNumber一樣,所有的NSDecimalNumber對象都是不可變額,這意味著在它們創(chuàng)建之后不能改變它們的值
Arithmetic算法
The main job of NSDecimalNumber is to provide fixed-point alternatives可供選擇的事物to C’s native原生arithmetic operations操作. All five of NSDecimalNumber’s arithmetic methods are demonstrated演示below在...下.
NSDecimalNumber的主要工作是提供可供選擇的定點(diǎn)算法給C的原生算法操作,全部的五個NSDecimalNumber的計算方法在下面被演示
NSDecimalNumber*price1 = [NSDecimalNumberdecimalNumberWithString:@"15.99"];NSDecimalNumber*price2 = [NSDecimalNumberdecimalNumberWithString:@"29.99"];NSDecimalNumber*coupon = [NSDecimalNumberdecimalNumberWithString:@"5.00"];NSDecimalNumber*discount = [NSDecimalNumberdecimalNumberWithString:@".90"];NSDecimalNumber*numProducts = [NSDecimalNumberdecimalNumberWithString:@"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];NSLog(@"Subtotal: %@", subtotal);// 45.98NSLog(@"After coupon: %@", afterCoupon);// 40.98NSLog((@"After discount: %@"), afterDiscount);// 36.882NSLog(@"Average price per product: %@", average);// 18.441NSLog(@"Average price squared: %@", averageSquared);// 340.070481
Unlike their floating-point counterparts相對物, these operations are guaranteed保證to be accurate精確. However, you’ll notice that many of the above calculations result in extra decimal places. Depending on the application, this may or may not be desirable (e.g., you might want to constrain約束currency values to 2 decimal places). This is where custom rounding湊整behavior comes in.
不像它們的相對物浮點(diǎn),這些操作保證了精確性,然而,你會注意到有很多超出計算結(jié)果的額外小數(shù)位,根據(jù)這個應(yīng)用,它們可能會也可能不會令人滿意(例如,你可能想約束貨幣值只有2個小數(shù)位),這是為什么自定義進(jìn)位行為被引入的原因
Rounding Behavior
// Rounding policies :// Original// value? ? 1.2? 1.21? 1.25? 1.35? 1.27// Plain? ? 1.2? 1.2? 1.3? 1.4? 1.3// Down? ? 1.2? 1.2? 1.2? 1.3? 1.2// Up? ? ? 1.2? 1.3? 1.3? 1.4? 1.3// Bankers? 1.2? 1.2? 1.2? 1.4? 1.3
Each of the above arithmetic methods have an alternate替換物withBehavior: form that let you define how the operation rounds the resulting value. The NSDecimalNumberHandler class encapsulates封裝a particular多有的,特別的rounding behavior and can be instantiated as follows:
每一個在上文中的計算方法有一個替換物---behavior:下面列出了讓你定義這個操作湊整這個結(jié)果的值,這個類封裝了一個特別的湊整行為,可以被實(shí)例化如下:
NSDecimalNumberHandler *roundUp = [NSDecimalNumberHandlerdecimalNumberHandlerWithRoundingMode:NSRoundUpscale:2raiseOnExactness:NOraiseOnOverflow:NOraiseOnUnderflow:NOraiseOnDivideByZero:YES];
The NSRoundUp argument屬性makes all operations round up to the nearest place. Other rounding options選項(xiàng)are NSRoundPlain, NSRoundDown, and NSRoundBankers, all of which are defined by NSRoundingMode. The scale: parameter參數(shù)defines the number of decimal places the resulting value should have, andthe rest of其余的the parameters參數(shù)define the exception-handling behavior of any operations. In this case, NSDecimalNumber will only raise an exception if you try to divide by zero.
NSRoundUp屬性使所有的操作算到最近的位置,其他的進(jìn)位選項(xiàng)是NSRoundPlain,NSRoundDown, 和NSRoundBankers,它們都被定義在NSRoundingMode,scale參數(shù)定義了結(jié)果值保留的小數(shù)位的數(shù)量,其余的參數(shù)給所有的操作定義了異常處理行為,這這個例子中,NSDecimalNumber將只捕獲一個異常,如果你嘗試除0.
This rounding behavior can then be passed to the decimalNumberByMultiplyingBy:withBehavior: method (or any of the other arithmetic methods), as shown below.
這個湊整的行為可以在之后被調(diào)用通過decimalNumberByMultiplyingBy:withBehavior:這個方法(或者任何其他的計算方法),如下所示.
NSDecimalNumber*subtotal = [NSDecimalNumberdecimalNumberWithString:@"40.98"];NSDecimalNumber*discount = [NSDecimalNumberdecimalNumberWithString:@".90"];NSDecimalNumber*total = [subtotal decimalNumberByMultiplyingBy:discount? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? withBehavior:roundUp];NSLog(@"Rounded total: %@", total);
Now, instead of 36.882, the total gets rounded up to two decimal points, resulting in 36.89.
現(xiàn)在,代替36.882,這個total算到2個小數(shù)位,結(jié)果是36.89
Comparing NSDecimalNumbers
Like NSNumber, NSDecimalNumber objects should use the compare: method instead of the native inequality不等operators. Again, this ensures that values are compared, even if they are stored存儲于in different instances. For example:
像NSNumber,NSDecimalNumber對象應(yīng)該用compare:方法代替原生的不等式操作,此外,這確保了值被比較,即使他們存儲于不通的實(shí)例中,例如
NSDecimalNumber*discount1 = [NSDecimalNumberdecimalNumberWithString:@".85"];NSDecimalNumber*discount2 = [NSDecimalNumberdecimalNumberWithString:@".9"];NSComparisonResultresult = [discount1 compare:discount2];if(result ==NSOrderedAscending) {NSLog(@"85%% < 90%%小于"); }elseif(result ==NSOrderedSame) {NSLog(@"85%% == 90%%等于");}elseif(result ==NSOrderedDescending) {NSLog(@"85%% > 90%%大于");}
NSDecimalNumber also inherits繼承the isEqualToNumber: method from NSNumber.
NSDecimalNumber也從NSNumber中繼承了isEqualToNumber:
Decimal Numbers in C
For most practical實(shí)用purposes目的, the NSDecimalNumber class should satisfy滿足your fixed-point needs; however, it’s worth noting that there is also a function-based alternative available可用in pure純C. This provides increased efficiency效率over the OOP interface discussed above and is thus preferred優(yōu)先選擇for high-performance性能applicationsdealing with處理a large number of calculations.
對于大多數(shù)實(shí)用的目的,NSDecimalNumber應(yīng)該能滿足你定點(diǎn)的需要,然而,值得注意的是也有一個基于純C語言的基礎(chǔ)函數(shù),它相對面向?qū)ο缶幊烫峁┝诵试谏厦娴挠懻撝?因此我們優(yōu)先選擇它為了一個高性能的應(yīng)用處理一個大數(shù)的計算
NSDecimal
Instead of an NSDecimalNumber object, the C interface is built around the NSDecimal struct. Unfortunately, the Foundation Framework doesn’t make it easy to create an NSDecimal from scratch. You need to generate生成one from a full-fledged成熟的NSDecimalNumber using its decimalValue method. There is a corresponding相應(yīng)的factory工廠method, also shown below.
代替NSDecimalNumber對象,C實(shí)例創(chuàng)建了一個NSDecimal結(jié)構(gòu)體,不幸的,Foundation Framework沒有使它很容易的創(chuàng)建從scratch,你需要去生成一個從一個成熟的NSDecimalNumber用它的decimalValue方法,它是一個相應(yīng)的工廠方法,也被展示如下
NSDecimalNumber*price = [NSDecimalNumberdecimalNumberWithString:@"15.99"];NSDecimalasStruct = [price decimalValue];NSDecimalNumber*asNewObject = [NSDecimalNumberdecimalNumberWithDecimal:asStruct];
This isn’t exactly準(zhǔn)確的an ideal理想way to create NSDecimal’s, but once you have a struct representation of your initial初始values, you canstick to堅(jiān)持the functional API presented below. All of these functions use struct’s as inputs and outputs.
它不是一個準(zhǔn)確的理想的方法去創(chuàng)建一個NSDecimal’s,但是一旦你有一個結(jié)構(gòu)展現(xiàn)了你的初始值,你可以一直堅(jiān)持這個功能API被提出,所有的函數(shù)用struct作為輸入和輸出
Arithmetic Functions
In lieu of代替the arithmetic methods of NSDecimalNumber, the C interface uses functions like NSDecimalAdd(), NSDecimalSubtract(), etc. Instead of returning the result, these functions populate填入the first argument with the calculated value. This makes it possible to reuse an existing NSDecimal in several operations and avoid allocating分配unnecessary structs just to hold intermediary媒介values.
代替計算方法的是NSDecimalNumber,C的接口用函數(shù)像NSDecimalAdd(),NSDecimalSubtract()等.代替結(jié)果的返回值,這個函數(shù)填入了第一個參數(shù)用一個可計算的值,這使它可以重用一個存在的NSDecimal在幾個操作,避免分配不必要的結(jié)構(gòu)體僅僅是為了保存媒介值
For example, the following snippet片段uses a single result variable across 5 function calls. Compare this to the Arithmetic section, which created a new NSDecimalNumber object for each calculation.
例如,以下的片段用一個結(jié)果變量被函數(shù)調(diào)用了5次,和算法節(jié)每一次計算都創(chuàng)建一個NSDecimalNumber做比較,
NSDecimalprice1 = [[NSDecimalNumberdecimalNumberWithString:@"15.99"] decimalValue];NSDecimalprice2 = [[NSDecimalNumberdecimalNumberWithString:@"29.99"] decimalValue];NSDecimalcoupon = [[NSDecimalNumberdecimalNumberWithString:@"5.00"] decimalValue];NSDecimaldiscount = [[NSDecimalNumberdecimalNumberWithString:@".90"] decimalValue];NSDecimalnumProducts = [[NSDecimalNumberdecimalNumberWithString:@"2.0"] decimalValue]NSLocale*locale = [NSLocalecurrentLocale];NSDecimalresult;NSDecimalAdd(&result, &price1, &price2,NSRoundUp);NSLog(@"Subtotal: %@",NSDecimalString(&result, locale));NSDecimalSubtract(&result, &result, &coupon,NSRoundUp);NSLog(@"After coupon: %@",NSDecimalString(&result, locale));NSDecimalMultiply(&result, &result, &discount,NSRoundUp);NSLog(@"After discount: %@",NSDecimalString(&result, locale));NSDecimalDivide(&result, &result, &numProducts,NSRoundUp);NSLog(@"Average price per product: %@",NSDecimalString(&result, locale));NSDecimalPower(&result, &result,2,NSRoundUp);NSLog(@"Average price squared: %@",NSDecimalString(&result, locale));
Notice that these functions accept references to NSDecimal structs, which is why we need to use the reference operator (&) instead of passing them directly. Also note that rounding is an inherent固有的,與生俱來的part of each operation—it’s not encapsulated in a separate分開entity單獨(dú)實(shí)體like NSDecimalNumberHandler.
主意到這些函數(shù)接受一個NSDecimal結(jié)構(gòu)體的引用,這是為什么我們需要用一個取址符(&)代替直接使用它們,也主意到湊整是每一個操作固有的一部分,它沒有像NSDecimalNumberHandler被封裝在一個分開的單獨(dú)實(shí)體中
The NSLocale instance defines the formatting格式化of NSDecimalString(), and is discussed討論more thoroughly徹底in the Dates module.
NSLocale實(shí)例定義了NSDecimalString的格式化,討論的更徹底在日期模塊中
Error Checking
Unlike their OOP counterparts相對物, the arithmetic functions don’t raise exceptions when a calculation error occurs發(fā)生. Instead, they follow the common C pattern of using the return value to indicate表明,象征success or failure. All of the above上文的functions return an NSCalculationError, which defines what kind of error occurred. The potential可能的scenarios情景are demonstrated演示below.
不想它們的相對物面向?qū)ο缶幊?這個計算函數(shù)在計算錯誤發(fā)生時不會捕獲異常,代替的是,它們允許普通的C模式用一個返回值去表明成功或者失敗,所有上文的函數(shù)返回了一個NSCalculationError,它定義了發(fā)生了什么錯誤,這個可能的情景如下
NSDecimala = [[NSDecimalNumberdecimalNumberWithString:@"1.0"] decimalValue];NSDecimalb = [[NSDecimalNumberdecimalNumberWithString:@"0.0"] decimalValue];NSDecimalresult;NSCalculationErrorsuccess =NSDecimalDivide(&result, &a, &b,NSRoundPlain);switch(success) {caseNSCalculationNoError:NSLog(@"Operation successful");break;caseNSCalculationLossOfPrecision:NSLog(@"Error: Operation resulted in loss of precision");break;caseNSCalculationUnderflow:NSLog(@"Error: Operation resulted in underflow");break;caseNSCalculationOverflow:NSLog(@"Error: Operation resulted in overflow");break;caseNSCalculationDivideByZero:NSLog(@"Error: Tried to divide by zero");break;default:break;}
Comparing NSDecimals
Comparing NSDecimal’s works exactly正是like the OOP interface, except you use the NSDecimalCompare() function:
比較NSDecimals的工作正是面向?qū)ο缶幊痰膶?shí)例,除非你用NSDecimalCompare()這個函數(shù)
NSDecimaldiscount1 = [[NSDecimalNumberdecimalNumberWithString:@".85"] decimalValue];NSDecimaldiscount2 = [[NSDecimalNumberdecimalNumberWithString:@".9"] decimalValue];NSComparisonResultresult =NSDecimalCompare(&discount1, &discount2);if(result ==NSOrderedAscending) {NSLog(@"85%% < 90%%");}elseif(result ==NSOrderedSame) {NSLog(@"85%% == 90%%");}elseif(result ==NSOrderedDescending) {NSLog(@"85%% > 90%%");}