iOS 高精度計算NSDecimalNumber簡單封裝

關(guān)于NSDecimalNumber

// Immutable with no mutable subclasses
@interface NSDecimalNumber : NSNumber

NSDecimalNumber屬于NSNumber的一個子類锤悄,并且是不可變的
官方介紹
NSDecimalNumber, an immutable subclass of NSNumber, provides an object-oriented wrapper for doing base-10 arithmetic. An instance can represent any number that can be expressed as mantissa x 10^exponent where mantissa is a decimal integer up to 38 digits long, and exponent is an integer from –128 through 127.
翻譯一下
NSDecimalNumber是NSNumber的一個不可變子類隔盛,它提供了一個面向?qū)ο蟮陌b器衙傀,用于執(zhí)行10進制算術(shù)。實例可以表示任何可以表示為尾數(shù)x 10^指數(shù)的數(shù)字被济,其中尾數(shù)是長達38位的十進制整數(shù)偷崩,指數(shù)是從-128到127的整數(shù)谴古。

說白了,就是用于高精度計算

NSDecimalNumber初始化

實例方法初始化

// 使用給定的尾數(shù)认罩、指數(shù)和符號初始化十進制數(shù)箱蝠。
- (instancetype)initWithMantissa:(unsigned long long)mantissa exponent:(short)exponent isNegative:(BOOL)flag;
// 初始化十進制數(shù)以表示給定的十進制數(shù)。
- (instancetype)initWithDecimal:(NSDecimal)dcm NS_DESIGNATED_INITIALIZER;
// 初始化十進制數(shù),使其值等價于給定數(shù)字字符串中的值宦搬。
- (instancetype)initWithString:(nullable NSString *)numberValue;
- (instancetype)initWithString:(nullable NSString *)numberValue locale:(nullable id)locale;

類方法初始化

// 使用給定的尾數(shù)牙瓢、指數(shù)和符號初始化十進制數(shù)。
+ (NSDecimalNumber *)decimalNumberWithMantissa:(unsigned long long)mantissa exponent:(short)exponent isNegative:(BOOL)flag;
+ (NSDecimalNumber *)decimalNumberWithDecimal:(NSDecimal)dcm;
+ (NSDecimalNumber *)decimalNumberWithString:(nullable NSString *)numberValue;
+ (NSDecimalNumber *)decimalNumberWithString:(nullable NSString *)numberValue locale:(nullable id)locale;

這么幾個初始化方法间校,看著還是這兩個實用

- (instancetype)initWithString:(nullable NSString *)numberValue;
+ (NSDecimalNumber *)decimalNumberWithString:(nullable NSString *)numberValue;

NSDecimalNumber運算

系統(tǒng)方法中提供了6種運算方式矾克,分別為

// 加
- (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;
// 指數(shù)
- (NSDecimalNumber *)decimalNumberByMultiplyingByPowerOf10:(short)power;
- (NSDecimalNumber *)decimalNumberByMultiplyingByPowerOf10:(short)power withBehavior:(nullable id <NSDecimalNumberBehaviors>)behavior;

關(guān)于NSDecimalNumberBehaviors這個協(xié)議,看看官方解釋
A protocol that declares three methods that control the discretionary aspects of working with decimal numbers.
The scale and roundingMode methods determine the precision of NSDecimalNumber’s return values and the way in which those values should be rounded to fit that precision. The exceptionDuringOperation:error:leftOperand:rightOperand: method determines the way in which an NSDecimalNumber object should handle different calculation errors.
For an example of a class that adopts the NSDecimalBehaviors protocol, see the specification for NSDecimalNumberHandler.

scale和roundingMode方法決定了NSDecimalNumber返回值的精度憔足,以及這些值應該四舍五入以適應該精度的方式胁附。方法決定了NSDecimalNumber對象處理不同計算錯誤的方式。
有關(guān)采用NSDecimalBehaviors協(xié)議的類的示例四瘫,請參閱NSDecimalNumberHandler的規(guī)范汉嗽。

意思就是,NSDecimalNumberBehaviors這個東西可以控制數(shù)字的舍取方式找蜜,和保留的小數(shù)位數(shù)

再看看NSDecimalNumberHandler這個饼暑,官方解釋

A class that adopts the decimal number behaviors protocol.// 采用十進制數(shù)字行為協(xié)議的類。

This class allows you to set the way an NSDecimalNumber object rounds off and handles errors, without having to create a custom class.
You can use an instance of this class as an argument to any of the NSDecimalNumber methods that end with ...Behavior:. If you don’t think you need special behavior, you probably don’t need this class—it is likely that NSDecimalNumber's default behavior will suit your needs.

這個類允許你設置NSDecimalNumber對象舍入和處理錯誤的方式洗做,而不需要創(chuàng)建一個自定義類弓叛。
你可以使用這個類的實例作為任何以…行為結(jié)尾的NSDecimalNumber方法的參數(shù)。如果你認為你不需要特殊的行為诚纸,你可能不需要這個類——NSDecimalNumber的默認行為很可能會滿足你的需求撰筷。

說白了,就是系統(tǒng)給了你一個NSDecimalNumberBehaviors協(xié)議的實例畦徘,直接用這個去實現(xiàn)NSDecimalNumberBehaviors協(xié)議就行了

// 默認的規(guī)則
/*
rounding mode :NSRoundPlain
scale :全精度(是多少位小數(shù)就是多少位)
忽略異常情況
*/
@property (class, readonly, strong) NSDecimalNumberHandler *defaultDecimalNumberHandler;
// 自定義舍取模式毕籽,保留的小數(shù)位數(shù),是否拋溢出等情況
- (instancetype)initWithRoundingMode:(NSRoundingMode)roundingMode scale:(short)scale raiseOnExactness:(BOOL)exact raiseOnOverflow:(BOOL)overflow raiseOnUnderflow:(BOOL)underflow raiseOnDivideByZero:(BOOL)divideByZero NS_DESIGNATED_INITIALIZER;

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

舉個??

// 保留兩位小數(shù)井辆,四舍五入
NSDecimalNumberHandler *handler = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode: NSRoundPlain
                                                                                         scale:2
                                                                              raiseOnExactness:NO
                                                                               raiseOnOverflow:NO
                                                                              raiseOnUnderflow:NO
                                                                           raiseOnDivideByZero:YES];

這個類介紹完了关筒,接下來就是進行封裝

NSDecimalNumber封裝

說是封裝,還不如說就是把調(diào)用簡化一下
為了方便這里創(chuàng)建一個NSString的分類杯缺,直接將字符串轉(zhuǎn)換成NSDecimalNumber類型的

// .h
/// 轉(zhuǎn)換為NSDecimalNumber數(shù)據(jù)
@property (nonatomic, copy, readonly) NSDecimalNumber *decimalValue;

// .m
- (NSDecimalNumber *)decimalValue {
    return [NSDecimalNumber decimalNumberWithString:self];
}

思想是用函數(shù)式編程來實現(xiàn)
首先創(chuàng)建一個NSDecimalNumber的分類就叫DecimalNumber
然后定義一些屬性分別處理加蒸播、減、乘萍肆、除袍榆、冪次方、指數(shù)塘揣、舍入方式
NSDecimalNumber+DecimalNumber.h

/// 加
@property (nonatomic, copy, readonly) NSDecimalNumber *_Nullable(^add)(id number);
/// 減
@property (nonatomic, copy, readonly) NSDecimalNumber *_Nullable(^reduce)(id number);
/// 乘
@property (nonatomic, copy, readonly) NSDecimalNumber *_Nullable(^multiplied)(id number);
/// 除 如果除數(shù)為0包雀,返回自身
@property (nonatomic, copy, readonly) NSDecimalNumber *_Nullable(^divide)(id number);
/// 冪次方
@property (nonatomic, copy, readonly) NSDecimalNumber *_Nullable(^raising)(NSUInteger power);
/// 指數(shù)
@property (nonatomic, copy, readonly) NSDecimalNumber *_Nullable(^multiplyingByPower)(short power);
/// 舍入
@property (nonatomic, copy, readonly) NSDecimalNumber *_Nullable(^rounding)(NSRoundingMode roundingMode, short scale);

NSDecimalNumber+DecimalNumber.m

/// 加
- (NSDecimalNumber * _Nullable (^)(id _Nonnull))add {
    return ^NSDecimalNumber *(id number){
        if ([number isKindOfClass:[NSDecimalNumber class]]) {
            return [self decimalNumberByAdding:(NSDecimalNumber *)number];
        } else if ([number isKindOfClass:[NSString class]]) {
            return [self decimalNumberByAdding:((NSString *)number).decimalValue];
        } else {
            return [self decimalNumberByAdding:[number stringValue].decimalValue];
        }
    };
}
/// 減
- (NSDecimalNumber * _Nullable (^)(id _Nonnull))reduce {
    return ^NSDecimalNumber *(id number){
        if ([number isKindOfClass:[NSDecimalNumber class]]) {
            return [self decimalNumberBySubtracting:(NSDecimalNumber *)number];
        } else if ([number isKindOfClass:[NSString class]]) {
            return [self decimalNumberBySubtracting:((NSString *)number).decimalValue];
        } else {
            return [self decimalNumberBySubtracting:[number stringValue].decimalValue];
        }
    };
}

/// 乘
- (NSDecimalNumber * _Nullable (^)(id _Nonnull))multiplied {
    return ^NSDecimalNumber *(id number){
        if ([number isKindOfClass:[NSDecimalNumber class]]) {
            return [self decimalNumberByMultiplyingBy:(NSDecimalNumber *)number];
        } else if ([number isKindOfClass:[NSString class]]) {
            return [self decimalNumberByMultiplyingBy:((NSString *)number).decimalValue];
        } else {
            return [self decimalNumberByMultiplyingBy:[number stringValue].decimalValue];
        }
    };
}

/// 除
- (NSDecimalNumber * _Nullable (^)(id _Nonnull))divide {
    return ^NSDecimalNumber *(id number){
        if ([number isKindOfClass:[NSDecimalNumber class]]) {
            return [self decimalNumberByDividingBy:(NSDecimalNumber *)number];
        } else if ([number isKindOfClass:[NSString class]]) {
            return [self decimalNumberByDividingBy:((NSString *)number).decimalValue];
        } else {
            return [self decimalNumberByDividingBy:[number stringValue].decimalValue];
        }
    };
}

/// 冪次方
- (NSDecimalNumber * _Nullable (^)(NSUInteger))raising {
    return ^NSDecimalNumber *(NSUInteger power){
        return [self decimalNumberByRaisingToPower:power];
    };
}

/// 指數(shù)
- (NSDecimalNumber * _Nullable (^)(short))multiplyingByPower {
    return ^NSDecimalNumber *(short power){
        return [self decimalNumberByMultiplyingByPowerOf10:power];
    };
}

/// 舍取方式
- (NSDecimalNumber * _Nullable (^)(NSRoundingMode, short))rounding {
    return ^NSDecimalNumber *(NSRoundingMode roundingMode, short scale){
        NSDecimalNumberHandler *handler = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:roundingMode
                                                                                                 scale:scale
                                                                                      raiseOnExactness:NO
                                                                                       raiseOnOverflow:NO
                                                                                      raiseOnUnderflow:NO
                                                                                   raiseOnDivideByZero:YES];
        return [self decimalNumberByRoundingAccordingToBehavior:handler];
    };
}

這樣就簡單的封裝好了
接下來看看使用

NSDecimalNumber *number = @"10.15".decimalValue;
NSDecimalNumber *newNumber = number.add(@"10.015").reduce(@"0.005").multiplied(@"1.01").divide(@"10").rounding(NSRoundPlain, 2);

解釋:(10.15 + 10.015 - 0.005) x 1.01 / 10的結(jié)果保留兩位小數(shù)四舍五入
結(jié)果:2.040000

DecimalNumber[10561:175487] 2.040000

有需要的,麻煩點個贊亲铡,謝謝

完整代碼

NSDecimalNumber+DecimalNumber.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSString (DecimalNumber)

/// 轉(zhuǎn)換為NSDecimalNumber數(shù)據(jù)
@property (nonatomic, copy, readonly) NSDecimalNumber *decimalValue;

@end

@interface NSDecimalNumber (DecimalNumber)

/// 加
@property (nonatomic, copy, readonly) NSDecimalNumber *_Nullable(^add)(id number);
/// 減
@property (nonatomic, copy, readonly) NSDecimalNumber *_Nullable(^reduce)(id number);
/// 乘
@property (nonatomic, copy, readonly) NSDecimalNumber *_Nullable(^multiplied)(id number);
/// 除 如果除數(shù)為0馏艾,返回自身
@property (nonatomic, copy, readonly) NSDecimalNumber *_Nullable(^divide)(id number);
/// 冪次方
@property (nonatomic, copy, readonly) NSDecimalNumber *_Nullable(^raising)(NSUInteger power);
/// 指數(shù)
@property (nonatomic, copy, readonly) NSDecimalNumber *_Nullable(^multiplyingByPower)(short power);
/// 舍入
@property (nonatomic, copy, readonly) NSDecimalNumber *_Nullable(^rounding)(NSRoundingMode roundingMode, short scale);

@end

NS_ASSUME_NONNULL_END

NSDecimalNumber+DecimalNumber.m

#import "NSDecimalNumber+DecimalNumber.h"

@implementation NSString (DecimalNumber)

- (NSDecimalNumber *)decimalValue {
    return [NSDecimalNumber decimalNumberWithString:self];
}

@end

@implementation NSDecimalNumber (DecimalNumber)

/// 加
- (NSDecimalNumber * _Nullable (^)(id _Nonnull))add {
    return ^NSDecimalNumber *(id number){
        if ([number isKindOfClass:[NSDecimalNumber class]]) {
            return [self decimalNumberByAdding:(NSDecimalNumber *)number];
        } else if ([number isKindOfClass:[NSString class]]) {
            return [self decimalNumberByAdding:((NSString *)number).decimalValue];
        } else {
            return [self decimalNumberByAdding:[number stringValue].decimalValue];
        }
    };
}

/// 減
- (NSDecimalNumber * _Nullable (^)(id _Nonnull))reduce {
    return ^NSDecimalNumber *(id number){
        if ([number isKindOfClass:[NSDecimalNumber class]]) {
            return [self decimalNumberBySubtracting:(NSDecimalNumber *)number];
        } else if ([number isKindOfClass:[NSString class]]) {
            return [self decimalNumberBySubtracting:((NSString *)number).decimalValue];
        } else {
            return [self decimalNumberBySubtracting:[number stringValue].decimalValue];
        }
    };
}

/// 乘
- (NSDecimalNumber * _Nullable (^)(id _Nonnull))multiplied {
    return ^NSDecimalNumber *(id number){
        if ([number isKindOfClass:[NSDecimalNumber class]]) {
            return [self decimalNumberByMultiplyingBy:(NSDecimalNumber *)number];
        } else if ([number isKindOfClass:[NSString class]]) {
            return [self decimalNumberByMultiplyingBy:((NSString *)number).decimalValue];
        } else {
            return [self decimalNumberByMultiplyingBy:[number stringValue].decimalValue];
        }
    };
}

/// 除
- (NSDecimalNumber * _Nullable (^)(id _Nonnull))divide {
    return ^NSDecimalNumber *(id number){
        if ([number isKindOfClass:[NSDecimalNumber class]]) {
            return [self decimalNumberByDividingBy:(NSDecimalNumber *)number];
        } else if ([number isKindOfClass:[NSString class]]) {
            return [self decimalNumberByDividingBy:((NSString *)number).decimalValue];
        } else {
            return [self decimalNumberByDividingBy:[number stringValue].decimalValue];
        }
    };
}

/// 冪次方
- (NSDecimalNumber * _Nullable (^)(NSUInteger))raising {
    return ^NSDecimalNumber *(NSUInteger power){
        return [self decimalNumberByRaisingToPower:power];
    };
}

/// 指數(shù)
- (NSDecimalNumber * _Nullable (^)(short))multiplyingByPower {
    return ^NSDecimalNumber *(short power){
        return [self decimalNumberByMultiplyingByPowerOf10:power];
    };
}

/// 舍取方式
- (NSDecimalNumber * _Nullable (^)(NSRoundingMode, short))rounding {
    return ^NSDecimalNumber *(NSRoundingMode roundingMode, short scale){
        NSDecimalNumberHandler *handler = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:roundingMode
                                                                                                 scale:scale
                                                                                      raiseOnExactness:NO
                                                                                       raiseOnOverflow:NO
                                                                                      raiseOnUnderflow:NO
                                                                                   raiseOnDivideByZero:YES];
        return [self decimalNumberByRoundingAccordingToBehavior:handler];
    };
}

@end

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末劳曹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子琅摩,更是在濱河造成了極大的恐慌铁孵,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件房资,死亡現(xiàn)場離奇詭異蜕劝,居然都是意外死亡,警方通過查閱死者的電腦和手機轰异,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門岖沛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人搭独,你說我怎么就攤上這事婴削。” “怎么了牙肝?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵唉俗,是天一觀的道長。 經(jīng)常有香客問我配椭,道長虫溜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任股缸,我火速辦了婚禮衡楞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘敦姻。我一直安慰自己瘾境,他們只是感情好,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布镰惦。 她就那樣靜靜地躺著迷守,像睡著了一般。 火紅的嫁衣襯著肌膚如雪陨献。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天懂更,我揣著相機與錄音眨业,去河邊找鬼。 笑死沮协,一個胖子當著我的面吹牛龄捡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播慷暂,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼聘殖,長吁一口氣:“原來是場噩夢啊……” “哼晨雳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起奸腺,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤餐禁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后突照,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帮非,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年讹蘑,在試婚紗的時候發(fā)現(xiàn)自己被綠了末盔。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡座慰,死狀恐怖陨舱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情版仔,我是刑警寧澤游盲,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站邦尊,受9級特大地震影響背桐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蝉揍,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一链峭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧又沾,春花似錦弊仪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至滑燃,卻和暖如春役听,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背表窘。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工典予, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人乐严。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓瘤袖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親昂验。 傳聞我的和親對象是個殘疾皇子捂敌,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容