NSNumberFormatter numberFromString在转换某些数字时会添加一小部分

时间:2016-03-16 19:03:57

标签: ios type-conversion nsnumberformatter

我在iPhone应用程序中看到一些奇怪的错误,我已经缩小到使用NSNumberFormatter。

一个精简的例子......

在Xcode游乐场,我有:

let numberFormatter = NSNumberFormatter()
//numberFormatter.numberStyle = .DecimalStyle - does not change behavior

let numberString = "546000.06"

let number: NSNumber = numberFormatter.numberFromString(numberString)!

print("number: \(number)")

let number1: NSNumber = NSDecimalNumber(string: numberString)

print("number1: \(number1)")

这是输出:

number: 546000.0600000001
number1: 546000.06

请注意,将numberStyle设置为.DecimalStyle并不会改变任何内容。

此问题仅发生在某些数值上(例如,8.03是另一个数值)。我认为NSNumberFormatter对于这种类型的转换是安全的,我在互联网上没有看到很多关于这个问题的噪音,所以我想假设这是我做错了。

任何人都可以解释我所看到的吗?非常感谢任何帮助!

2 个答案:

答案 0 :(得分:1)

看起来NSNumberFormatter存在问题。有一些值可以使此舍入误差逐渐增加。在XCode 7.2.1游乐场中,它显示大约8.03。

我解决这个问题的一种方法是舍入十进制数。由于差值为+/-很小,因此四舍五入到四分之一的位置应该可行。您可以使用各种舍入模式。在这个例子中,我使用了RoundPlain。

var initialValue = NSDecimalNumber(string: "7")
let handler = NSDecimalNumberHandler(roundingMode: NSRoundingMode.RoundPlain, scale: 4, raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: false) 
let numberFormatter = NSNumberFormatter()

for index in 1...300 {    
    initialValue = initialValue.decimalNumberByAdding(0.01)
    let stringValue = "\(initialValue)"

    var number = numberFormatter.numberFromString(stringValue)
    var decimalNumber: NSDecimalNumber = NSDecimalNumber(decimal: number!.decimalValue)
    decimalNumber = decimalNumber.decimalNumberByRoundingAccordingToBehavior(handler)

    print("stringValue = \(stringValue), decimalNumber = \(decimalNumber), number = \(number!)")
}

答案 1 :(得分:0)

您看到了舍入错误。

如果您想向用户显示两位小数位数,请设置maximumFractionDigits

let numberFormatter = NSNumberFormatter()
numberFormatter.maximumFractionDigits = 2

let firstNumber = NSNumber(float:546000.06)
let firstNumberString = numberFormatter.stringFromNumber(firstNumber)!

let secondNumber = NSNumber(float:5.1337)
let secondNumberString = numberFormatter.stringFromNumber(secondNumber)!

print("firstNumber: \(firstNumberString)")
print("secondNumber: \(secondNumberString)")

输出将是:

"firstNumber: 546000.06\n"
"secondNumber: 5.13\n"

如果您尝试从字符串中解析一个数字,那么您已经完成了设置。

let thirdNumber = numberFormatter.numberFromString("546000.06")!
print("thirdNumber \(thirdNumber.className): \(thirdNumber)")

最后一行打印对象本身的描述(带有舍入错误):

"thirdNumber __NSCFNumber: 546000.0600000001\n"

更新(2016-03-18)

您想要解析以下字符串:

let currencyString = "$546,000.06"

首先,我们创建一个带有locale.CurrencyStyle的格式化程序。请注意,locale取决于您尝试解析的字符串,而不取决于您运行的系统。

let currencyFormatter = NSNumberFormatter.init()
currencyFormatter.locale = NSLocale.init(localeIdentifier: "en_US")
currencyFormatter.numberStyle = .CurrencyStyle

我们现在可以使用格式化程序来删除字符串。

let currencyNumber = currencyFormatter.numberFromString(currencyString)!

您现在可以自由存储此对象,例如在CoreData中。但是,如果您不想将值呈现给用户(或在控制台上打印),则 使用NSNumberFormatter。如果直接打印对象,则使用对象的description(或debugDescription)。

因此,让我们创建另一个格式化程序来打印NSNumber对象的值:

let outputFormatter = NSNumberFormatter.init()
outputFormatter.maximumFractionDigits = 10

使用多个小数位数字应该打印任何可能的舍入错误。但是

print("Number: \(outputFormatter.stringFromNumber(currencyNumber)!)")

输出所需的结果:

"Number: 546000.06\n"

您所看到的是NSNumber对象(description / debugDescription)的内部表示结果。