NSDecimalNumberを使って数値処理を行う #備忘録

# NSDecimalNumber

NSDecimalNumberとは10進数を表すオブジェクトです。**NSNumber**を継承しており、**四則演算** を行うためのラッパーを提供しています。インスタンス自体は、仮数×10^(指数)として表示可能な数値を表します。それぞれ、仮数は10進数の38桁整数、指数は-128〜127の整数を扱うことができます。

## 四則演算
#### 加法
" + "の代わりに**decimalNumberByAdding**メソッドを使用する。

```objc:Adding
  // result = first + second;
  NSDecimalNumber *result = [first decimalNumberByAdding:second];
```

#### 減法
" - "の代わりに**decimalNumberBySubtracting**メソッドを使用する。

````objc:Subtracting
  // result = first - second;
  [first decimalNumberBySubtracting:second];
```

#### 乗法 
" ＊ "の代わりに**decimalNumberByMultiplyingBy**メソッドを使用する。

```objc:Multiplying
  // result = first * second;
  [first decimalNumberByMultiplyingBy:second];
```

#### 除法
" / "の代わりに**decimalNumberByDividingBy**メソッドを使用する。

```objc:Dividing
  // result = first / second;
  [first decimalNumberByDividingBy:second];
```

### その他の演算

#### [累乗を求める](http://cocoaapi.hatenablog.com/entry/00130604/Foundation_NSDecimalNumber_decimalNumberByRaisingToPower_)
10進数の値を累乗（2乗や3乗など）した結果を返す。
第１引数は**NSUInteger型**で指定する。

```objc:累乗
  // dNum^(power) : dNumのpower乗
  [dNum decimalNumberByRaisingToPower:power];
```

###### (例) 2の10乗を求める場合

```
- (void)doPowerUp {
    NSDecimalNumber *dNum2 = [NSDecimalNumber decimalNumberWithString:@"2"];
    NSDecimalNumber *dNum10 = [dNum1 decimalNumberByRaisingToPower:10];

    NSLog(@"result = %@",[dNum10 description]);  //  result = 1024
}
```

#### １０の累乗を乗算した値を求める
**decimalNumberByMultiplyingByPowerOf10**メソッドを使用する。
###### (例) 4の10乗に5.6を乗法した値を求める場合

```objc:
  NSDecimalNumber *dNum4 = [NSDecimalNumber decimalNumberWithString:@"5.6"];
  
  // result = 5.6 * 10^4;
  NSDecimalNumber *result = [dNum4 decimalNumberByMultiplyingByPowerOf10:4]; 
```


## 値にアクセスする

#### レシーバのNSDecimalの数値（読み込みのみ）

```
  NSDecimalNumber *dNum7 = [NSDecimalNumber decimalNumberWithString:@"7"];
  NSDecimal *dnum7 = dNum7.decimalValue;
```

#### 10進数オブジェクトの近似値のdoubleの数値（読み込みのみ）

```
   NSDecimalNumber *dNum10 = [NSDecimalNumber DecimalNumberWithString:@"10"];
   double num10 = dNum10.doubleValue;
```

### 最大値と最小値

#### 1. maximumDecimalNumber
NSDecimalNumber型が扱うことができる最大値を取得する。

```objc:
   NSDecimalNumber *maximum = NSDecimalNumber.maximumDecimalNumber;
```

#### 2. minimumDecimalNumber
NSDecimalNumber型が扱うことができる最小値を取得する。

```objc:
   NSDecimalNumber *minimum = NSDecimalNumber.maximumDecimalNumber;
```


#### 3. notANumber
数ではない十進数オブジェクトを作って返す。

```
NSNumber *aNA = [NSDecimalNumber notANumber];
```

###### (例)

```objc:
  NSDecimalNumber *Num = ....
  [[NSDcimalNumber notANumber] isEqualToNumber:Num];
```

#### 4. "1"を作る
1の値を持つNSDecimalNumberオブジェクトを作成する。

```
  NSDecimalNumber *dNum1 = NSDecimalNumber.one;
```

#### 5. "0"を作る
0の値を持つNSDecimalNumberオブジェクトを作成する。

```
  NSDecimalNumber *dNum0 = NSDecimalNumber.zero;
```

 

### NSString列からNSDecimalNumberオブジェクトを作成する
###### (例)文字列"7"からNSDecimalNumber型を作成する。

```
  NSDecimalNumber *dNum7 = [NSDecimalNumber decimalNumberWithString:@"7"];
```

### NSDecimalNumberオブジェクトからNSString型を作成する
###### (例)NSDecimalNumber型"7"から文字列を作成する。

```objc:Sample1.m
  NSDecimalNumber *dNum7 = [NSDecimalNumber decimalNumberWithString:@"7"];
  NSString *str7 = [NSString stringWithFormat:@"%f",dNum7.doubleValue];
```

```objc:Sample2.m
  NSDecimalNumber *dNum7 = [NSDecimalNumber decimalNumberWithString:@"7"];
  NSString *str7 = dNum7.stringValue;
```



### 10進数からNSDecimalNumberオブジェクトを作成する
###### (例)

```
  NSNumber *num = [NSNumber numberWithFloat:233.333];
  NSDecimalNumber *dNum = [NSDecimalNumber decimalNumberWithDecimal:[num decimalValue]];
```


## 10進数計算の精度や丸め方を指定する

### 小数点を含む数値
小数点を含む数値を計算する場合には系統的な誤差が生じます。詳しくは[こちら](http://msdn.microsoft.com/ja-jp/library/c151dt3s(v=vs.80).aspx)を参考にしてください。


```
-(NSDecimalNumber *)decimalNumberByRoundingAccordingToBehavior:(id <NSDecimalNumberBehaviors>)behavior:
```

丸めた十進数オブジェクトを返します。レシーバが3.141592でデフォルトの丸めを使えば3.14を返します。

### NSDecimalNumberHandler

浮動小数点がある場合には、小数点を丸める処理が必要になる場合があります。この場合、**NSDecimalNumberHandlerクラス**を使用します。数値の丸め方**decimalNumberHandlerWithRoundingModeメソッド**を使用してカスタム設定します。

```objc:example1.m

+ (NSDecimalNumber *)convertScaleWithDecimalNumber:(NSString *)aNumber 
                                             scale:(NSInteger)aScale {
    
    if (!aNumber) return [[NSDecimalNumber alloc] initWithString:@"0"];
    NSDecimalNumber *decimalValue = [[NSDecimalNumber alloc] initWithString:aNumber];
    NSDecimalNumberHandler *roundingStyle 
       = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain                                   
                                                                scale:aScale
                                                     raiseOnExactness:NO
                                                      raiseOnOverflow:NO
                                                     raiseOnUnderflow:NO
                                                  raiseOnDivideByZero:NO];

    return [decimalValue decimalNumberByRoundingAccordingToBehavior:roundingStyle];
}

```


```objc:example2.m
+ (NSDecimalNumber *)getScaleWithDecimalNumber:(NSDecimalNumber *)aNumber 
                                         scale:(NSInteger)aScale {
    NSDecimalNumberHandler *roundingStyle 
       = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain
                                                                scale:aScale
                                                     raiseOnExactness:NO
                                                      raiseOnOverflow:NO
                                                     raiseOnUnderflow:NO
                                                  raiseOnDivideByZero:NO];

    return [aNumber decimalNumberByRoundingAccordingToBehavior:roundingStyle];
}

```


|  引数 |  引数名  |  |
|:---------------:|:------------:|:------------|
|     第1引数     | roundingMode| NSRoundPlain：四捨五入 NSRoundDown：切り下げNSRoundUp：切り上げ|
|     第2引数     | scale               | 丸めた結果の桁数                     |
|     第3引数     | raiseOnExactness    | 精度が狂った場合、エラーを発生させるか     |
|     第4引数     | raiseOnOverflow     | オーバーフロー場合、エラーを発生させるか    |
|     第5引数     | raiseOnUnderflow    | アンダーフローした場合、エラーを発生させるか  |
|     第6引数     | raiseOnDivideByZero | 0で除算した場合、エラーを発生させるか      |



## NSDecimalNumberBehaviorsプロトコル

http://stackoverflow.com/questions/10907009/objective-c-using-nsdecimalnumberhandler-and-nsdecimalnumberbehaviors-protoco

<Foundation/NSDecimalNumber.h>


### 適合するプロトコル
1. NSDecimalNumberBehaviors
– roundingMode
– scale
– exceptionDuringOperation:error:leftOperand:rightOperand:

2. NSCoding
– encodeWithCoder:
– initWithCoder:

### 十進数ハンドラの作成
+ defaultDecimalNumberHandler
デフォルトの十進数ハンドラを作って返します

+ decimalNumberHandlerWithRoundingMode: scale: raiseOnExactness 
  raiseOnOverflow: raiseOnUnderflow: raiseOnDivideByZero
カスタマイズされた十進数オブジェクトの計算の動作を作って返します

### 十進数ハンドラの初期化
– initWithRoundingMode: scale: raiseOnExactness
　　　　　raiseOnOverflow:raiseOnUnderflow:raiseOnDivideByZero
カスタマイズされた十進数オブジェクトの計算の動作を初期化して返します


# Swiftとの関係

Foundation.frameworkに関して、Swiftは表面上ではNSDecimalNumberクラスとの橋渡しがされており、10進数のしくみを提供しています。10進数の値型は、機能面においてNSDecimalNumber参照型と同じものが提供されているため、Objective-CのAPIと相互作用するようなSwiftコードの場合、両者はどちらでも利用可能です。この振る舞いは、標準的な文字列、数値、あるいはFoundationクラスに対応しているcollection型とのSwiftの橋渡し方法と似ています。


# 参考

https://developer.apple.com/reference/foundation/nsdecimalnumber?language=objc
http://cocoaapi.hatenablog.com/entry/10010412/NSDecimalNumber
http://hidef.jp/post-449/