iOS---Foundation(NSDecimalNumber.h)

 一、简介

iOS中需要精确计算时,double, float类型往往会出现不可预料的问题:

iOS---Foundation(NSDecimalNumber.h)_第1张图片

在调试过程中可以看到 doule 类型 d3 在计算过程中值不是预计的9999.99

如果在要用中计算涉及到金额等重要数据,不建议使用这种不可控结果的方式,可以采用iOS提供的另外一种支持准确精度计算的数据类型 NSDecimalNumber.

NSDecimalNumberNSNumber的子类,比NSNumber的功能更为强大,可以指定一个数的幂,四舍五入等操作。由于NSDecimalNumber精度较高,所以会比基本数据类型费时,所以需要权衡考虑,苹果官方建议在货币以及要求精度很高的场景下使用。

所有NSDecimalNumber对象是不可变的,这意味着已经被创建后不能改变它们的值

二、NSDecimalNumber.h

NS_ASSUME_NONNULL_BEGIN


/*************** Exceptions ***********/


FOUNDATION_EXPORT NSString * const NSDecimalNumberExactnessException;

FOUNDATION_EXPORT NSString * const NSDecimalNumberOverflowException;

FOUNDATION_EXPORT NSString * const NSDecimalNumberUnderflowException;

FOUNDATION_EXPORT NSString * const NSDecimalNumberDivideByZeroException;


/*************** Rounding and Exception behavior ***********/


@class NSDecimalNumber;


@protocol NSDecimalNumberBehaviors


- (NSRoundingMode)roundingMode;


- (short)scale;

    // The scale could return NO_SCALE for no defined scale.


- (nullable NSDecimalNumber *)exceptionDuringOperation:(SEL)operation error:(NSCalculationError)error leftOperand:(NSDecimalNumber *)leftOperand rightOperand:(nullable NSDecimalNumber *)rightOperand;

    // Receiver can raise, return a new value, or return nil to ignore the exception.


@end


/*************** NSDecimalNumber: the class ***********/


@interface NSDecimalNumber : NSNumber {

@private

    signed   int _exponent:8;

    unsigned int _length:4;

    unsigned int _isNegative:1;

    unsigned int _isCompact:1;

    unsigned int _reserved:1;

    unsigned int _hasExternalRefCount:1;

    unsigned int _refs:16;

    unsigned short _mantissa[0]; /* GCC */

}


- (instancetype)initWithMantissa:(unsigned long long)mantissa exponent:(short)exponent isNegative:(BOOL)flag;

- (instancetype)initWithDecimal:(NSDecimal)dcm NS_DESIGNATED_INITIALIZER;

- (instancetype)initWithString:(nullable NSString *)numberValue;

- (instancetype)initWithString:(nullable NSString *)numberValue locale:(nullable id)locale;


- (NSString *)descriptionWithLocale:(nullable id)locale;


@property (readonly) NSDecimal decimalValue;

/*

     a×10ⁿ 为例:

     mantissa:无符号长整型。表示a

     exponent:短整形。幂级数n

     flag:符号,YES:前面带负号。-a×10ⁿ

                NO:前面无符号。a×10ⁿ

     eg:

     NSDecimalNumber*num20=[NSDecimalNumber decimalNumberWithMantissa:12 exponent:2 isNegative:YES];//-1200

     NSDecimalNumber*num21=[NSDecimalNumber decimalNumberWithMantissa:12 exponent:2 isNegative:NO];//1200

     NSDecimalNumber*num22=[NSDecimalNumber decimalNumberWithMantissa:12.6 exponent:2 isNegative:YES];//-1200

     NSDecimalNumber*num23=[NSDecimalNumber decimalNumberWithMantissa:12.6 exponent:2 isNegative:NO];//1200

*/

+ (NSDecimalNumber *)decimalNumberWithMantissa:(unsigned long long)mantissa exponent:(short)exponent isNegative:(BOOL)flag;

+ (NSDecimalNumber *)decimalNumberWithDecimal:(NSDecimal)dcm;

//字符串值转NSDecimalNumber

+ (NSDecimalNumber *)decimalNumberWithString:(nullable NSString *)numberValue;

+ (NSDecimalNumber *)decimalNumberWithString:(nullable NSString *)numberValue locale:(nullable id)locale;


+ (NSDecimalNumber *)zero;//0

+ (NSDecimalNumber *)one;//1

+ (NSDecimalNumber *)minimumDecimalNumber;//平台所能表达的最小数(有符号)

+ (NSDecimalNumber *)maximumDecimalNumber;//平台所能表达的最大数(有符号)

+ (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;

//power次幂计算(power表示几次幂)

- (NSDecimalNumber *)decimalNumberByRaisingToPower:(NSUInteger)power;

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

//乘以10power次幂(power表示幂级)

- (NSDecimalNumber *)decimalNumberByMultiplyingByPowerOf10:(short)power;

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


//指定一些有效信息,例如四舍五入的情况,小数后保留位数的情况以及数据溢出或除以零的情况等。

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


//比较(返回NSComparisonResult的枚举值)

- (NSComparisonResult)compare:(NSNumber *)decimalNumber;

 


+ (void)setDefaultBehavior:(id <NSDecimalNumberBehaviors>)behavior;


+ (id <NSDecimalNumberBehaviors>)defaultBehavior;

    // One behavior per thread - The default behavior is

    //   rounding mode: NSRoundPlain

    //   scale: No defined scale (full precision)

    //   ignore exactnessException

    //   raise on overflow, underflow and divide by zero.


@property (readonly) const char *objCType NS_RETURNS_INNER_POINTER;

    // return 'd' for double

    

@property (readonly) double doubleValue;

    // return an approximate double value

    

@end


/************************A class for defining common behaviors ****************************************/

//用于指定一些有效信息,例如四舍五入的情况,小数后保留位数的情况以及数据溢出或除以零的情况等。

//exactoverflowunderflowdivideByZeroNO时,在出现上溢,下溢,除数为零时,返回NaN(可能是notANumber的意思),程序不会崩溃。

//当值为YES时,出现对应的情况程序直接崩掉。

@interface NSDecimalNumberHandler : NSObject <NSDecimalNumberBehaviors, NSCoding> {

  @private

    signed int _scale:16;

    unsigned _roundingMode:3;

    unsigned _raiseOnExactness:1;

    unsigned _raiseOnOverflow:1;

    unsigned _raiseOnUnderflow:1;

    unsigned _raiseOnDivideByZero:1;

    unsigned _unused:9;

    void *_reserved2;

    void *_reserved;

}

+ (NSDecimalNumberHandler *)defaultDecimalNumberHandler;

    // rounding mode: NSRoundPlain

    // scale: No defined scale (full precision)

    // ignore exactnessException (return nil)

    // raise on overflow, underflow and divide by zero.

- (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;


@end


/**************************************Extensions to other classes*********************************/

@interface NSNumber (NSDecimalNumberExtensions)


@property (readonly) NSDecimal decimalValue;

    // Could be silently inexact for float and double.

@end


@interface NSScanner (NSDecimalNumberScanning)


- (BOOL)scanDecimal:(nullable NSDecimal *)dcm;


@end


NS_ASSUME_NONNULL_END



三、用法

(1)四舍五入截取指定小数位的值

//四舍五入截取指定小数位的值(price:目标数据  position:有效小数位) 返回截取后数据

/*

    typedef NS_ENUM(NSUInteger, NSRoundingMode) {

        NSRoundPlain,   //用于四舍五入

        NSRoundDown,    //只舍不入

        NSRoundUp,      //不舍只入

        NSRoundBankers  //。。。取最精确的,当遇到5时,舍和入是一样的,所以可以有两个值。(待指教)

    };

Original Value

NSRoundPlain

NSRoundDown

NSRoundUp

NSRoundBankers

1.24

1.2

1.2

1.3

1.2

1.26

1.3

1.2

1.3

1.3

1.25

1.3

1.2

1.3

1.2

1.35

1.4

1.3

1.4

1.4

–1.35

–1.4

–1.4

–1.3

–1.4

*/

+(NSString *)notRounding:(float)price afterPoint:(NSInteger)position

{

    NSDecimalNumberHandler* roundingBehavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:position raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];

    NSDecimalNumber *ouncesDecimal;

    NSDecimalNumber *roundedOunces;

    

    ouncesDecimal = [[NSDecimalNumber alloc] initWithFloat:price];

    roundedOunces = [ouncesDecimal decimalNumberByRoundingAccordingToBehavior:roundingBehavior];

    return [NSString stringWithFormat:@"%@",roundedOunces];

}


你可能感兴趣的:(iOS---Foundation(NSDecimalNumber.h))