最近项目中遇到金额计算,CGFloat类型金额计算的过程中,让我吃了不少苦头,先来看下面几行代码:
示例代码
CGFloat var1 = 120.66;
CGFloat var2 = 120.66;
CGFloat var3 = [@"120.66" floatValue];
CGFloat var4 = [@"120.66" floatValue];
CGFloat var5 = 120.67 - 0.01;
CGFloat var6 = [@"120.67" floatValue] - 0.01;
bool flag1 = var1 == var2;
bool flag2 = var3 == var4;
bool flag3 = var3 == var5;
bool flag4 = var5 == var6;
bool flag5 = var3 == var6;
略去猜答案的过程,直接看结果
图一现象总结
- var1 和var2相等
- var3 和var4相等
- NSString 的 floatValue 会让CGFloat 的精度增加
- 精度确定的float类型做运算,精度确定 var5 = 120.66
- 不同精度的float类型做运算,精度会丢失 var6 = 120.65999816894531
浮点数相等问题
CGFloat x = var5 - var6;
const CGFloat EPSINON = 0.000001;
if (( x >= -EPSINON ) && ( x <= EPSINON ))
{
NSLog("var5 和var6相等");
}
鉴于CGFloat在处理金额过程中会遇到精度问题,iOS中提供了NSDecimalNumber类来处理精度问题,如果要四舍五入的话还要使用另一个类 NSDecimalNumberHandler
NSDecimalNumber类
/*
讲述下参数的含义:
RoundingMode: 简单讲就是你要四舍五入操作的标准.
typedef NS_ENUM(NSUInteger, NSRoundingMode) {
NSRoundPlain, // 精度位后四舍五入
NSRoundDown, // 精度位后舍去
NSRoundUp, // 精度位后入上去
NSRoundBankers // 精度位后的数值等于5时,分两种情况:5后面还有其他数字(非0),则进位后舍去;若5后面是0(即5是最后一位),则根据5前一位数的奇偶性来判断是否需要进位,奇数进位,偶数舍去。
};
// 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
scale : 需要保留的精度。
raiseOnExactness : 为YES时在处理精确时如果有错误,就会抛出异常。
raiseOnOverflow : YES时在计算精度向上溢出时会抛出异常,否则返回。
raiseOnUnderflow : YES时在计算精度向下溢出时会抛出异常,否则返回.
raiseOnDivideByZero : YES时。当除以0时会抛出异常,否则返回。
*/
保留2位小数
NSDecimalNumberHandler *roundUp = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundPlain
scale:2
raiseOnExactness:NO
raiseOnOverflow:NO
raiseOnUnderflow:NO
raiseOnDivideByZero:YES];
计算金额并四舍五入
NSDecimalNumber *originMoney = [NSDecimalNumber decimalNumberWithString:num];//通过字符串计算金额
NSDecimalNumber *roundMoney = [subtotal decimalNumberByRoundingAccordingToBehavior:roundUp];//按照设定的规则计算出金额
金额计算实例
NSDecimalNumber *varDecimal1 = [NSDecimalNumber decimalNumberWithString:@"120.661"];//120.661
NSDecimalNumber *varDecimal2 = [NSDecimalNumber decimalNumberWithString:@"120.670"];//120.670
//加
[varDecimal1 decimalNumberByAdding:varDecimal2];
//减
NSDecimalNumber *varDecimal3 = [[varDecimal2 decimalNumberBySubtracting:varDecimal1] decimalNumberByRoundingAccordingToBehavior:roundUp];//0.01
//乘
[varDecimal1 decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithString:@"0.005"]];
//除
[varDecimal1 decimalNumberByDividingBy:varDecimal2];
//比较
[varDecimal1 compare:varDecimal2] == NSOrderedAscending //varDecimal1小于varDecimal2
总结
- 金额相关,后台要传字符串到前台
- 计算过程中要用NSDecimalNumber来防止计算过程中出现精度错误
- [@"120.67" floatValue] - 0.01 不等于120.66
网友评论