title: NSDecimalNumber的介绍和使用!
date: 2016-11-07 15:51:56
categories: 笔记
tags:NSDecimalNumber、计算精度
NSDecimalNumber的介绍和使用!
前言
在iOS的精度计算过程,只要你使用浮点型来进行运算不管如何都无法躲避精度缺失的可能性。而苹果对此也提供了更加精准的精度计算的类:NSDecimalNumber
NSDecimalNumber
- NSDecimalNumber是NSNumber的不可变子类。苹果针对浮点型计算时存在精度计算误差的问题而提供的一个计算类,它是基于10进制的定点计算保证了精度不会缺失。同时也可以定制精度的取正类型:向上取正、向下去正、四舍五入等。相对与浮点类型的计算,NSDecimalNumber提供了更加精准的计算。
一个定点数包含了:用一个尾数(Mantissa)、一个基数(Base)、一个指数(Exponent)以及一个表示正负的符号(sign).
比如 15.99 用十进制科学计数法可以表达为 +1599 × 10⁻² ,其中 1.2345 为尾数,10 为基数,2 为指数。sign为 ‘+’。
代码表示:
NSDecimalNumber *price;
price = [NSDecimalNumber decimalNumberWithMantissa:1599
exponent:-2
isNegative:NO];
price = [NSDecimalNumber decimalNumberWithString:@"15.99"];
// 或者直接由字符串生成NSDecimalNumber
price = [NSDecimalNumber decimalNumberWithString:@"15.99"];
- 算式:
基本的加减乘除用法入戏:
NSDecimalNumber *price1 = [NSDecimalNumber decimalNumberWithString:@"15.99"];
NSDecimalNumber *price2 = [NSDecimalNumber decimalNumberWithString:@"29.99"];
NSDecimalNumber *coupon = [NSDecimalNumber decimalNumberWithString:@"5.00"];
NSDecimalNumber *discount = [NSDecimalNumber decimalNumberWithString:@".90"];
NSDecimalNumber *numProducts = [NSDecimalNumber decimalNumberWithString:@"2.0"];
NSDecimalNumber *subtotal = [price1 decimalNumberByAdding:price2];
NSDecimalNumber *afterCoupon = [subtotal decimalNumberBySubtracting:coupon];
NSDecimalNumber *afterDiscount = [afterCoupon decimalNumberByMultiplyingBy:discount];
NSDecimalNumber *average = [afterDiscount decimalNumberByDividingBy:numProducts];
NSDecimalNumber *averageSquared = [average decimalNumberByRaisingToPower:2];
NSLog(@"Subtotal: %@", subtotal); // 45.98
NSLog(@"After coupon: %@", afterCoupon); // 40.98
NSLog((@"After discount: %@"), afterDiscount); // 36.882
NSLog(@"Average price per product: %@", average); // 18.441
NSLog(@"Average price squared: %@", averageSquared); // 340.070481
- 取整方式
上面的没个算式方法都有个扩充的带behavior参数的方法。通过behavior可以对最后的结果进行取整和保留小数个数的限制。你可以自定义behavior:
// Rounding policies :
// Original
// value 1.2 1.21 1.25 1.35 1.27
// Plain 1.2 1.2 1.3 1.4 1.3
// Down 1.2 1.2 1.2 1.3 1.2
// Up 1.2 1.3 1.3 1.4 1.3
// Bankers 1.2 1.2 1.2 1.4 1.3
typedef NS_ENUM(NSUInteger, NSRoundingMode) {
NSRoundPlain, // Round up on a tie
NSRoundDown, // Always down == truncate
NSRoundUp, // Always up
NSRoundBankers // on a tie round so last digit is even
};
//scale:保留有效小数的个数(为0的无效小数后自动过滤).
//Exactness:进度异常、Overflow:向上溢出、Underflow:向下溢出、DivideByZero:除数为0。当参数为YES出错会抛出异常,为NO时忽略异常。返回nil.
NSDecimalNumberHandler *roundUp = [NSDecimalNumberHandler
decimalNumberHandlerWithRoundingMode:NSRoundUp
scale:2
raiseOnExactness:NO
raiseOnOverflow:NO
raiseOnUnderflow:NO
raiseOnDivideByZero:YES];
PS:NSDecimalNumber同时提供了isEqualToNumber:方法和NSNumber进行判断是否相等。
NSNumberFormatter
当NSDecimalNumber的计算结果为整数时,NSDecimalNumber并不会保留无效的小数,会直接忽略设置的scale。直接返回整数。如:scale设置为2。但是1.00*100结果返回100,并不会返回100.00。无法得到自己想要的设置的值。
所以在使用NSDecimalNumber得到计算结果时,可使用NSNumberFormatter来格式化计算的结果。自定义NSNumberFormatter可以来适应不同的开发的需求,另:NSNumberFormatter同时可以用来做国际化的数字显示!
首先了解下NSNumberFormatter,NSNumberFormatter从iOS 7.0 开始是线程安全的。支持多线程的操作!NSNumberFormatter根据自定义的格式将NSString——>格式化的NSNumber,NSNumber ——>格式化的NSString.
NSNumberFormatter 继承于 NSFormatter!
在用于格式化主要起作用的属性:
usesSignificantDigits:是否使用有效数字控制
usesSignificantDigits:YES一下两个属性有效,NO会忽略下面俩个。
minimumSignificantDigits:最小的有效数
maximumSignificantDigits:最大的有效数(超过这个值,结果会按照Bankers的方式进位)
alwaysShowsDecimalSeparator:小数点是否一直显示
minimumIntegerDigits: 整数位最小的个数
maximumIntegerDigits: 整数位最大的个数
minimumFractionDigits: 小数位的最小个数(会以0填充)
maximumFractionDigits:小数位的最大个数
alwaysShowsDecimalSeparator:是否一直显示小数分割符号
decimalSeparator:自定义分割的符号(默认".")
currencyDecimalSeparator:货币运算(NSNumberFormatterCurrencyStyle)时分割符号。
NSNumberFormatterStyle:
NSNumberFormatterNoStyle 平常计算类型
NSNumberFormatterDecimalStyle 计算结果会带“,”
NSNumberFormatterCurrencyStyle 货币运算(会根据地区变化)
NSNumberFormatterPercentStyle 百分比结果带“%”
NSNumberFormatterScientificStyle 科学计算法结果
NSNumberFormatterSpellOutStyle 对数字文本化(会根据地区变化)
iOS 9之后可用
NSNumberFormatterOrdinalStyle NS_ENUM_AVAILABLE(10_11, 9_0) 按照排名
NSNumberFormatterCurrencyISOCodeStyle NS_ENUM_AVAILABLE(10_11, 9_0) 使用ISO 4217 货币代码前缀显示
NSNumberFormatterCurrencyPluralStyle NS_ENUM_AVAILABLE(10_11, 9_0) 货币复数形式
NSNumberFormatterCurrencyAccountingStyle NS_ENUM_AVAILABLE(10_11, 9_0) 会计类型
};
不同类型结果
在 en_US_POSIX locale:
Number String
NSNumberFormatterNoStyle: 1234.5678 -> “1235”.
NSNumberFormatterDecimalStyle: 1234.5678 ->“1234.5678”.
NSNumberFormatterCurrencyStyle:1234.5678 -> “$ 1234.57”.
NSNumberFormatterPercentStyle: 1234.5678 ->“123457%”.
NSNumberFormatterScientificStyle:1234.5678 -> “1.234568E+003”.
NSNumberFormatterSpellOutStyle: 1234.5678 -> “one thousand two hundred thirty-four point five six seven eight”.
NSNumberFormatterOrdinalStyle:1234.5678 -> “1,235th”
NSNumberFormatterCurrencyISOCodeStyle:1234.5678 -> “USD 1234.57”.
NSNumberFormatterCurrencyPluralStyle: 1234.5678 -> “1234.57 US dollars”.
NSNumberFormatterCurrencyAccountingStyle:
1234.5678 -> “$1234.57”. 与 NSNumberFormatterCurrencyStyle
不同的是负号的处理 -1234.5678-> “($1,234.57)”.
最后提供一个轮子,针对NSString 和NSNumber 的精度计算:
github地址