如何修复NSString转NSDecimalNumber的decimalNumberWithString转换异常?
NSString转NSDecimalNumber的精度偏差问题
测试代码
NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: --------------------------------------------------------------------"); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.0001"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.0002"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.0003"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.0004"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.0005"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.0006"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.0007"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.0008"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.0009"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.001"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.002"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.003"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.004"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.005"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.006"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.007"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.008"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: %@",[NSDecimalNumber decimalNumberWithString:@"0.009"]); NSLog(@"calcuMax_test - calculateMaxForCross feeRateDecimal: --------------------------------------------------------------------");
输出结果
calcuMax_test - calculateMaxForCross feeRateDecimal: -------------------------------------------------------------------- calcuMax_test - calculateMaxForCross feeRateDecimal: 0.0001 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.0002 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.0003 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.0004 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.0005 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.0006000000000000001 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.0006999999999999999 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.0008 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.0009 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.001 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.002 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.003 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.004 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.005 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.006 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.006999999999999999 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.008 calcuMax_test - calculateMaxForCross feeRateDecimal: 0.008999999999999999 calcuMax_test - calculateMaxForCross feeRateDecimal: --------------------------------------------------------------------
问题
为何转换结果出现精度偏差?正确的转换方法是什么?
解答
精度偏差的原因
NSDecimalNumber本身是十进制精确浮点类型,转换@"0.0006"这类字符串时内部存储的值是完全精确的。你看到的精度偏差只是打印显示的问题:当用%@输出NSDecimalNumber时,系统会先把它转换成double类型,而double是二进制浮点类型,无法精确表示所有十进制小数(比如0.0006),所以输出时会出现近似值。
正确的处理方法
1. 用NSNumberFormatter格式化输出
如果只是要正确显示数值,使用NSNumberFormatter来指定小数位数和格式,避免自动转double带来的偏差:
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; formatter.numberStyle = NSNumberFormatterDecimalStyle; formatter.minimumFractionDigits = 4; formatter.maximumFractionDigits = 4; formatter.roundingMode = NSNumberFormatterRoundHalfUp; NSDecimalNumber *num = [NSDecimalNumber decimalNumberWithString:@"0.0006"]; NSLog(@"Formatted number: %@", [formatter stringFromNumber:num]);
输出会是0.0006,完全符合预期。
2. 直接用NSDecimalNumber做精确运算
如果需要进行计算,直接使用NSDecimalNumber提供的API(比如decimalNumberByAdding:、decimalNumberByMultiplyingBy:等),这些运算都是基于十进制的精确计算,不会有二进制浮点的精度问题。
内容的提问来源于stack exchange,提问作者won
相关产品推荐
相关产品推荐

