Help us understand the problem. What is going on with this article?

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

More than 3 years have 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/

Nick_paper
iOS Engineer
iridge
O2OやFinTechソリューションの企画・開発・運用をしています。
https://iridge.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away