iOS --NSDecimalNumber 處理計算精度不準確問題

Clemmie Lau發表於2018-07-02

前言:

       很少寫知識文章或技術文章,邁出第一步勇氣還是要的。加油。。。

正文:

       前幾天在專案開發的時候遇到double型別精度不準確導致程式碼不能按正常邏輯處理,這個讓我很納悶,因為前幾個版本都沒有出現這型別的錯。試過用NSString來處理,可是加減乘除還是要轉換成基本型別,又會計算精度誤差。

         問題如圖下:

     iOS --NSDecimalNumber 處理計算精度不準確問題

剛好有個判斷邏輯是:

 if (payAmount1 + payAmount2 < [paySumAmount doubleValue]) {
        UIAlert(@"提示",@"輸入的總金額不足以支付訂單,請重新輸入", @"確定", nil);
        return;
    }
複製程式碼

很尷尬,明明輸入的金額滿足支付,卻一直被被攔住。

        為了避免偶爾性出現計算精度問題,用NSDecimalNumber 可以完美的處理。不多說上相關程式碼。

        1.關於NSDecimalNumber屬性和方法

/*
宣告:

mantissa:為基數
exponent:為冪的次方 例如 傳2 相等於 10*10
flag: yes代表正數,no代表負數
return:返回物件NSDecimalNumber
*/
- (instancetype)initWithMantissa:(unsigned long long)mantissa exponent:(short)exponent isNegative:(BOOL)flag;
+ (NSDecimalNumber *)decimalNumberWithMantissa:(unsigned long long)mantissa exponent:(short)exponent isNegative:(BOOL)flag;

+ (NSDecimalNumber *)decimalNumberWithString:(nullable NSString *)numberValue;
- (NSNumber *)initWithDouble:(double)value;
- (NSNumber *)initWithFloat:(float)value;
- (NSNumber *)initWithBool:(BOOL)value;
~
~
~

@property (class, readonly, copy) NSDecimalNumber *zero;//0
@property (class, readonly, copy) NSDecimalNumber *one;//1
@property (class, readonly, copy) NSDecimalNumber *minimumDecimalNumber;//最小值
@property (class, readonly, copy) NSDecimalNumber *maximumDecimalNumber;//最大值
@property (class, readonly, copy) NSDecimalNumber *notANumber;//無值

//加法
- (NSDecimalNumber *)decimalNumberByAdding:(NSDecimalNumber *)decimalNumber;
- (NSDecimalNumber *)decimalNumberByAdding:(NSDecimalNumber *)decimalNumber withBehavior:(nullable id <NSDecimalNumberBehaviors>)behavior;

//減法
- (NSDecimalNumber *)decimalNumberBySubtracting:(NSDecimalNumber *)decimalNumber;
- (NSDecimalNumber *)decimalNumberBySubtracting:(NSDecimalNumber *)decimalNumber withBehavior:(nullable id <NSDecimalNumberBehaviors>)behavior;

//乘法
- (NSDecimalNumber *)decimalNumberByMultiplyingBy:(NSDecimalNumber *)decimalNumber;
- (NSDecimalNumber *)decimalNumberByMultiplyingBy:(NSDecimalNumber *)decimalNumber withBehavior:(nullable id <NSDecimalNumberBehaviors>)behavior;

//除法
- (NSDecimalNumber *)decimalNumberByDividingBy:(NSDecimalNumber *)decimalNumber;
- (NSDecimalNumber *)decimalNumberByDividingBy:(NSDecimalNumber *)decimalNumber withBehavior:(nullable id <NSDecimalNumberBehaviors>)behavior;

//次方
- (NSDecimalNumber *)decimalNumberByRaisingToPower:(NSUInteger)power;
- (NSDecimalNumber *)decimalNumberByRaisingToPower:(NSUInteger)power withBehavior:(nullable id <NSDecimalNumberBehaviors>)behavior;

//冪運算
- (NSDecimalNumber *)decimalNumberByMultiplyingByPowerOf10:(short)power;
- (NSDecimalNumber *)decimalNumberByMultiplyingByPowerOf10:(short)power withBehavior:(nullable id <NSDecimalNumberBehaviors>)behavior;

//四捨五入
- (NSDecimalNumber *)decimalNumberByRoundingAccordingToBehavior:(nullable id <NSDecimalNumberBehaviors>)behavior;

//比較2個值
- (NSComparisonResult)compare:(NSNumber *)decimalNumber;
複製程式碼

  2.NSDecimalNumberHandler 用法 -- 宣告四捨五入規則

@property (class, readonly, strong) NSDecimalNumberHandler *defaultDecimalNumberHandler;
    // rounding mode: NSRoundPlain //預設NSRoundPlain
    // scale: No defined scale (full precision)//保留幾位小數
    // ignore exactnessException (return nil) //轉換異常丟擲
    // raise on overflow, underflow and divide by zero.//轉換異常丟擲


/**
NSRoundingMode:
{
NSRoundPlain:四捨五入   2.56 ->2.6   2.44 -> 2.4
NSRoundDown:向下取值    2.56->2.5    2.44->2.4
NSRoundUp:向上取值      2.56 ->2.6   2.44->2.5
NSRoundBankers:        NSRoundPlain一樣(如果大神們有其他特殊例子麻煩告訴一聲)
}

scale:保留幾個小數
其他:溢位異常報錯 為No就可以了。
*/
- (instancetype)initWithRoundingMode:(NSRoundingMode)roundingMode scale:(short)scale raiseOnExactness:(BOOL)exact raiseOnOverflow:(BOOL)overflow raiseOnUnderflow:(BOOL)underflow raiseOnDivideByZero:(BOOL)divideByZero

+ (instancetype)decimalNumberHandlerWithRoundingMode:(NSRoundingMode)roundingMode scale:(short)scale raiseOnExactness:(BOOL)exact raiseOnOverflow:(BOOL)overflow raiseOnUnderflow:(BOOL)underflow raiseOnDivideByZero:(BOOL)divideByZero;
複製程式碼

    3.簡單用法

  NSDecimalNumberHandler *roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:position raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
   
  NSDecimalNumber *decimal1 = [[NSDecimalNumber alloc] initWithDouble:15.384];
  NSDecimalNumber *decimal2 = [[NSDecimalNumber alloc] initWithDouble:13.555];
 
  //加法
  NSDecimalNumber *result = [decimal1 decimalNumberByAdding:decimal2 withBehavior:roundingBehavior];//28.94

  //減法
  NSDecimalNumber *result1 = [decimal1 decimalNumberBySubtracting:decimal2 withBehavior:roundingBehavior];//1.82

   //比較
   if ([result compare:result1] ==  NSOrderedAscending) { //升序
        //result < result1
   }else if ([result compare:result1] ==  NSOrderedDescending) { //降序
        //result > result1
   }else if ([result1 compare:result1] ==  NSOrderedSame) {
        //result = result1
   }
複製程式碼

 結束語

邊踩坑邊學習.蟹蟹


相關文章