iOS精确计算 货币计算

声明:本文章是本人汇总网络多篇文章改写,也可以说是转载!!!

在iOS开发中,和货币价格计算相关的,需要注意计算精度的问题。即使只是两位小数,也会出现误差。使用float和double类型运算,是完全不够的。系统提供的API的NSDecimalNumber专门用于进行此类操作。以下是一些基本的测试,
float a = 0.01;
int b = 99999999;
double c = 0.0;
c = a*b;
NSLog(@”%f”,c);
NSLog(@”%.2f”,c);
使用float和double类型,结果误差很大。

下面先普及一下相关知识

四舍五入VS银行家舍入

相信细心的程序员们早就发现了.net环境下默认舍入算法的是“四舍六入”的算法。从小学我们就学过“四舍五入”算法,但是更加科学的舍入办法应该是“四舍六入”,也就是今天我们要讨论的“银行家舍入”。
大家可以做一个Demo。
C#环境下
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: do
6: {
7: Console.WriteLine(“请输入一个小数回车测试,输入其他回车结束测试”);
8: string Num = Console.ReadLine();
9: try
10: {
11: Console.WriteLine(“结果为” + Convert.ToInt16(Convert.ToDouble(Num)));
12: }
13: catch (Exception e) {
14: break;
15: }
16: }
17: while (true );
18: }
19: }
得到的结果如下
这里写图片描述

VB.net环境下测试代码为
1: Sub Main()
2: Do
3: Console.WriteLine(“请输入一个小数回车测试,输入其他回车结束测试。”)
4: Try
5: Dim a As String = Console.ReadLine()
6: Console.WriteLine(“结果为:” & CInt(Convert.ToDouble(a)))
7: Catch ex As Exception
8: Exit Sub
9: End Try
10: Loop
11: End Sub
结果如下
这里写图片描述
完全符合银行家舍入的规律:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一
关于VB.net中的CInt微软的MSDN上有具体说明
Fractional Parts. When you convert a nonintegral value to an integral type, the integer conversion functions ( CByte, CInt, CLng, CSByte, CShort, CUInt, CULng, and CUShort) remove the fractional part and round the value to the closest integer.

If the fractional part is exactly 0.5, the integer conversion functions round it to the nearest even integer. For example, 0.5 rounds to 0, and 1.5 and 2.5 both round to 2. This is sometimes called banker’s rounding, and its purpose is to compensate for a bias that could accumulate when adding many such numbers together.

相对于四舍五入,银行家舍入的确更加的准确,讨论如下:
有些童鞋可能认为在一般性的测量中,最后一位小数位上0至9出现的概率是相等的。一共十个数字,0至4可以舍去(四舍),5至9可以进位(五入),多么完美的舍入算法!
但是!您可能忽略了一点,末尾的0在这里是相当于10还是相当于0?
为了避免混沌,请看下图:
这里写图片描述

图中是用Matlab画的一个简单的数轴,可以看出0.0、0.1、0.2、0.3、0.4、0.5、0.6、0.7、0.8、0.9、1.0中0.0和1.0都是0结尾所以不能确定测量数据中的0是哪个零!
还是看上图,图中只要不满0.5都按照0算,大于0.5都按照1.0算,那么剩下的0.5怎么办?为了体现平均性,看上一位是奇数还是偶数,如果是奇数则进位,如果是偶数则舍去。这正是“银行家舍入”的思想。这样一来便达到了相对于“四舍五入”舍入方法更加平衡的舍入算法。
PS:“银行家舍入”是 IEEE 规定的舍入标准。因此所有符合 IEEE 标准的语言都是采用这一算法的。
上文转自:http://www.cnblogs.com/beijiguangyong/

在普及一下苹果系统给的相关api

NSDecimalNumber是iOS系统的api,是一种精确计算。主要解决一下问题:
1、字符串转float等不精确问题。
2、精确计算
3、保留小数位数
4、四舍五入及其它的保留小数位数规则

NSDecimalNumber的运算
//1、字符串 -> NSDecimalNumber
NSDecimalNumber *num1 = [NSDecimalNumber decimalNumberWithString:@”123”];
NSLog(@”num1 == %@”,num1);
//2、NSNumber -> NSDecimalNumber(基础类型的话,先转成NSNumber)
NSDecimalNumber *num2 = [NSDecimalNumber decimalNumberWithDecimal:[[NSNumber numberWithInt:123] decimalValue]];
NSLog(@”num2 == %@”,num2);

//相关运算

/*加 +
- (NSDecimalNumber *)decimalNumberByAdding:(NSDecimalNumber *)decimalNumber;
- (NSDecimalNumber *)decimalNumberByAdding:(NSDecimalNumber *)decimalNumber withBehavior:(nullable id <NSDecimalNumberBehaviors>)behavior;//带保留小数位数
 */
NSDecimalNumber *num3 = [num1 decimalNumberByAdding:num2];
NSLog(@"num3 == %@",num3);

/*减 -
 - (NSDecimalNumber *)decimalNumberBySubtracting:(NSDecimalNumber *)decimalNumber;
 - (NSDecimalNumber *)decimalNumberBySubtracting:(NSDecimalNumber *)decimalNumber withBehavior:(nullable id <NSDecimalNumberBehaviors>)behavior;//带保留小数位数
 */
NSDecimalNumber *num4 = [num1 decimalNumberBySubtracting:num2];
NSLog(@"num4 == %@",num4);

/*乘 *
 - (NSDecimalNumber *)decimalNumberByMultiplyingBy:(NSDecimalNumber *)decimalNumber;
 - (NSDecimalNumber *)decimalNumberByMultiplyingBy:(NSDecimalNumber *)decimalNumber withBehavior:(nullable id <NSDecimalNumberBehaviors>)behavior;//带保留小数位数
 */
NSDecimalNumber *num5 = [num1 decimalNumberByMultiplyingBy:num2];
NSLog(@"num5 == %@",num5);

/*除 \
 - (NSDecimalNumber *)decimalNumberByDividingBy:(NSDecimalNumber *)decimalNumber;
 - (NSDecimalNumber *)decimalNumberByDividingBy:(NSDecimalNumber *)decimalNumber withBehavior:(nullable id <NSDecimalNumberBehaviors>)behavior;//带保留小数位数
 */
NSDecimalNumber *num6 = [num1 decimalNumberByDividingBy:num2];
NSLog(@"num6 == %@",num6);

/*比较 
 - (NSComparisonResult)compare:(NSNumber *)decimalNumber;
 */

NSComparisonResult result = [num1 compare:num2];
NSLog(@"result == %ld",(long)result);


//保留小数及相关参数含义
/*保留小数
 + (instancetype)decimalNumberHandlerWithRoundingMode:(NSRoundingMode)roundingMode scale:(short)scale raiseOnExactness:(BOOL)exact raiseOnOverflow:(BOOL)overflow raiseOnUnderflow:(BOOL)underflow raiseOnDivideByZero:(BOOL)divideByZero;
 */

  //1、 NSRoundingMode
// 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 貌似是:

    if(四舍五入位 == 5)(四舍五入位(5)+  保留位 )%2 == 0 ? 入 : 舍 ;

    if(四舍五入位 != 5) 遵从四舍五入
};
*/
//2、scale 保留小数个数
//3、其它:溢出等是否报错

NSDecimalNumber *num_1 = [NSDecimalNumber decimalNumberWithString:@"1.2"];
NSDecimalNumber *num_2 = [NSDecimalNumber decimalNumberWithString:@"3.4"];
NSDecimalNumberHandler *handel = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundBankers scale:2 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];

NSDecimalNumber *num_3 = [num_1 decimalNumberByDividingBy:num_2 withBehavior:handel];
NSLog(@"num_3 == %@",num_3);

//打印结果
/*
 2015-12-11 13:26:28.269 test[32280:9906455] num1 == 123
 2015-12-11 13:26:28.269 test[32280:9906455] num2 == 123
 2015-12-11 13:26:28.269 test[32280:9906455] num3 == 246
 2015-12-11 13:26:28.270 test[32280:9906455] num4 == 0
 2015-12-11 13:26:28.270 test[32280:9906455] num5 == 15129
 2015-12-11 13:26:28.270 test[32280:9906455] num6 == 1
 2015-12-11 13:26:28.270 test[32280:9906455] result == 0
 2015-12-11 13:26:28.270 test[32280:9906455] num_3 == 0.35
 */

结果总结之后写了这些代码:

// Add 加, subtract 减, multiply 乘, divide 除 NSDecimalNumberHandler * hander = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain scale:2 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:YES]; NSDecimalNumber * numRateF = [NSDecimalNumber decimalNumberWithString:[self.dataArray objectAtIndex:1]]; NSDecimalNumber * numRateE = [numRateF decimalNumberByDividingBy:[NSDecimalNumber decimalNumberWithString:@”10”]]; NSDecimalNumber * numTatal = [NSDecimalNumber decimalNumberWithString:self.totalAmtText.text]; NSDecimalNumber * numOther = [NSDecimalNumber decimalNumberWithString:self.otherAmtText.text]; NSDecimalNumber * numJian = [numTatal decimalNumberBySubtracting:numOther withBehavior:hander]; NSDecimalNumber * numCheng = [numJian decimalNumberByMultiplyingBy:numRateE withBehavior:hander]; NSDecimalNumber * numJia = [numCheng decimalNumberByAdding:numOther withBehavior:hander]; self.actAmtLabel.text = [[numJia stringValue] stringByAppendingString:@” 元”];

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值