Convert String to NSDecimalNumber
Asked Answered
G

3

13

I try to convert a String into a NSDecimalNumber here it my code:

class func stringToDecimal (dataToDecimal: String) -> NSDecimalNumber {

    let dataToDecimal = dataToDecimal.stringByReplacingOccurrencesOfString(",", withString: ".")
    print(dataToDecimal)
    let  decimalReturn = NSDecimalNumber(string: dataToDecimal)
    print(decimalReturn)

    if decimalReturn == NSDecimalNumber.notANumber(){
        return 0.00
    }
    return decimalReturn
}

First I thought that maybe the , is wrong but even with . it doesn't work. The first print (before converting) shows e.g 80,00 but the print(decimalReturn) shows only 80

the if line is the just to check if the result is not a number.

Guidepost answered 13/11, 2015 at 20:38 Comment(0)
I
23

Use an NSNumberFormatter to parse your input. Set its generatesDecimalNumbers property to true:

let formatter = NumberFormatter()
formatter.generatesDecimalNumbers = true

Here's how you use it, if you want to return 0 when the string can't be parsed:

func decimal(with string: String) -> NSDecimalNumber {
    return formatter.number(from: string) as? NSDecimalNumber ?? 0
}

decimal(with: "80.00")

// Result: 80 as an NSDecimalNumber

By default, the formatter will look at the device's locale setting to determine the decimal marker. You should leave it that way. For the sake of example, I'll force it to a French locale:

// DON'T DO THIS. Just an example of behavior in a French locale.
formatter.locale = Locale(identifier: "fr-FR")

decimal(with: "80,00")
// Result: 80

decimal(with: "80.00")
// Result: 0

If you really want to always use a comma as the decimal mark, you can set the decimalSeparator property:

formatter.decimalSeparator = ","
Ity answered 13/11, 2015 at 20:50 Comment(7)
it worked, but I had to do the let dataToDecimal = dataToDecimal.stringByReplacingOccurrencesOfString(",", withString: ".") first, to bring my string to get not nil. If I just try a string with a "," it will bring nil as result.Guidepost
I ve read that NSDecimal is precise for calculation, but if I take 80.01 the outcome is 80.01000002 if I take 80.10 it s 80.09999999. How would you reduce the digits to 2?Guidepost
You'll have to show your code. It would be best to do so as a new question.Ity
I wonder, how is this different from Decimal(string: "80,00", locale: Locale.current)?Apoenzyme
Decimal(string:locale:) was not added until iOS 10 and macOS 10.12 in 2016.Ity
@robmayoff I don't believe that's true. NSDecimalNumber had that initializer always. If you notice, all Decimal methods are marked as iOS 10+.Apoenzyme
You're right. I was looking at the Swift docs, and everything in the Decimal class is available as of the 2016 OS versions.Ity
W
4

There is nothing wrong with the way you are creating a NSDecimalNumber from a String. However, you might not need to worry about replacing the comma with a period if that is how your local formats numbers.

The reason it prints 80 instead of 80,00 or 80.00 is that print just uses the description property of the number. If you want to customize the formatting, you should setup your own NSNumberFormatter.

let number = NSDecimalNumber(string: "80.00")
print(number) // 80
let formatter = NSNumberFormatter()
formatter.minimumFractionDigits = 2
let string = formatter.stringFromNumber(number)
print(string) // 80.0
Weanling answered 13/11, 2015 at 21:10 Comment(2)
I tried out, but in real project and playground it brings an error message "Cannot convert value of type "String" to expected argument type "NSNumber" but the ...minimumFractions I like and will play with that.Guidepost
It's possible there is another problem in your code then. The code I posted works fine in a playground.Weanling
C
0

When server forces some certain locale use that instead of Locale.current lest you want breakage depending on the user's settings

extension String {
    var decimalFromServerString: NSDecimalNumber? {
        let parsed = NSDecimalNumber(string: self, locale: ru_ru_posix)
        if parsed == .notANumber {
            return nil
        }
        return parsed
    }
}

PS. if you hardcore locale to en_US POSIX for some reason string "..." comes out as 0. Go figure :-[

Coleen answered 27/8, 2019 at 11:47 Comment(1)
This doesn't seem to work if the number has comma thousands separators in itAramen

© 2022 - 2024 — McMap. All rights reserved.