Edited at

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

More than 1 year has passed since last update.


NSDecimalNumber

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


四則演算


加法

" + "の代わりにdecimalNumberByAddingメソッドを使用する。


Adding

  // result = first + second;

NSDecimalNumber *result = [first decimalNumberByAdding:second];


減法

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


Subtracting

  // result = first - second;

[first decimalNumberBySubtracting:second];


乗法

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


Multiplying

  // result = first * second;

[first decimalNumberByMultiplyingBy:second];


除法

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


Dividing

  // result = first / second;

[first decimalNumberByDividingBy:second];


その他の演算


累乗を求める

10進数の値を累乗(2乗や3乗など)した結果を返す。

第1引数はNSUInteger型で指定する。


累乗

  // 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
}


10の累乗を乗算した値を求める

decimalNumberByMultiplyingByPowerOf10メソッドを使用する。


(例) 4の10乗に5.6を乗法した値を求める場合

  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型が扱うことができる最大値を取得する。

   NSDecimalNumber *maximum = NSDecimalNumber.maximumDecimalNumber;


2. minimumDecimalNumber

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

   NSDecimalNumber *minimum = NSDecimalNumber.maximumDecimalNumber;


3. notANumber

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

NSNumber *aNA = [NSDecimalNumber notANumber];


(例)

  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"から文字列を作成する。


Sample1.m

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

NSString *str7 = [NSString stringWithFormat:@"%f",dNum7.doubleValue];


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進数計算の精度や丸め方を指定する


小数点を含む数値

小数点を含む数値を計算する場合には系統的な誤差が生じます。詳しくはこちらを参考にしてください。

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

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


NSDecimalNumberHandler

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


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];
}



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


適合するプロトコル


  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/