ChatGPT解决这个技术问题 Extra ChatGPT

Swift - How to convert String to Double

I'm trying to write a BMI program in swift language. And I got this problem: how to convert a String to a Double?

In Objective-C, I can do like this:

double myDouble = [myString doubleValue];

But how can I achieve this in Swift language?

This has changed in Swift 2, see the new answer using the new Double() failable initializer: stackoverflow.com/a/32850058/276626

J
Jarsen

Swift 2 Update There are new failable initializers that allow you to do this in more idiomatic and safe way (as many answers have noted, NSString's double value is not very safe because it returns 0 for non number values. This means that the doubleValue of "foo" and "0" are the same.)

let myDouble = Double(myString)

This returns an optional, so in cases like passing in "foo" where doubleValue would have returned 0, the failable intializer will return nil. You can use a guard, if-let, or map to handle the Optional<Double>

Original Post: You don't need to use the NSString constructor like the accepted answer proposes. You can simply bridge it like this:

(swiftString as NSString).doubleValue

ohai, Jarsen. One downside to this approach is that it will not fail if the string isn't a valid double. Instead you will just get 0.0. That is, the result of - [NSString doubleValue] is Double in swift as apposed to Double? (Optional<Double>). This is different from the Swift String function toInt() which returns Int? and states in the docs that it "...accepts strings that match the regular expression "[-+]?[0-9]+" only."
Yes, using .toInt() would be better. However, this was more an answer to how to do exactly the same thing, but in Swift.
This approach tightly couples you to Foundation, and likely won't work on any non-Apple implementations of Swift.
In Swift 2.0 there is Double.init?(_ text: String) failable initializer now available.
This is not the best approach. It will fail depending on the decimal separator . It will be fine with '.' but not with ','. NSNumberFormatter will support decimal separator used in the current localization.
P
Paul Solt

Swift 4.2+ String to Double

You should use the new type initializers to convert between String and numeric types (Double, Float, Int). It'll return an Optional type (Double?) which will have the correct value or nil if the String was not a number.

Note: The NSString doubleValue property is not recommended because it returns 0 if the value cannot be converted (i.e.: bad user input).

let lessPrecisePI = Float("3.14")

let morePrecisePI = Double("3.1415926536")
let invalidNumber = Float("alphabet") // nil, not a valid number

Unwrap the values to use them using if/let

if let cost = Double(textField.text!) {
    print("The user entered a value price of \(cost)")
} else {
    print("Not a valid number: \(textField.text!)")
}

You can convert formatted numbers and currency using the NumberFormatter class.

let formatter = NumberFormatter()
formatter.locale = Locale.current // USA: Locale(identifier: "en_US")
formatter.numberStyle = .decimal
let number = formatter.number(from: "9,999.99")

Currency formats

let usLocale = Locale(identifier: "en_US")
let frenchLocale = Locale(identifier: "fr_FR")
let germanLocale = Locale(identifier: "de_DE")
let englishUKLocale = Locale(identifier: "en_GB") // United Kingdom
formatter.numberStyle = .currency

formatter.locale = usLocale
let usCurrency = formatter.number(from: "$9,999.99")

formatter.locale = frenchLocale
let frenchCurrency = formatter.number(from: "9999,99€")
// Note: "9 999,99€" fails with grouping separator
// Note: "9999,99 €" fails with a space before the €

formatter.locale = germanLocale
let germanCurrency = formatter.number(from: "9999,99€")
// Note: "9.999,99€" fails with grouping separator

formatter.locale = englishUKLocale
let englishUKCurrency = formatter.number(from: "£9,999.99")

Read more on my blog post about converting String to Double types (and currency).


another issue is that it doesn't not support numbers in different locale other than the 1,000.00, if you use other formats like 1 000,00 it would cut off the 2 decimals at the end. At least this is whats happening for Objective-C. -- As per Apple documentation:(They are not locale-aware) The following convenience methods all skip initial space characters (whitespaceSet) and ignore trailing characters. They are not locale-aware. NSScanner or NSNumberFormatter can be used for more powerful and locale-aware parsing of numbers.
@mskw I've added additional details on using the NumberFormatter to parse currency. In my code I'll string together multiple formatter attempts to convert using the if/let syntax so that I can parse both currency formatted numbers ($1,000.00) or formatted numbers (1,000). You don't always know how the user will enter numbers, so being able to support both is ideal with a group of if/let statements.
Should use Double(myString) ?? 0.0 to avoid init errors
I want to use the expression in an if/let or guard let and have appropriate logic. That way you can display a proper user facing message about the format being incorrect. If you just treat it as 0, it can be confusing to the user.
K
Krunal Nagvadia

For a little more Swift feeling, using NSFormatter() avoids casting to NSString, and returns nil when the string does not contain a Double value (e.g. "test" will not return 0.0).

let double = NSNumberFormatter().numberFromString(myString)?.doubleValue

Alternatively, extending Swift's String type:

extension String {
    func toDouble() -> Double? {
        return NumberFormatter().number(from: self)?.doubleValue
    }
}

and use it like toInt():

var myString = "4.2"
var myDouble = myString.toDouble()

This returns an optional Double? which has to be unwrapped.

Either with forced unwrapping:

println("The value is \(myDouble!)") // prints: The value is 4.2

or with an if let statement:

if let myDouble = myDouble {
    println("The value is \(myDouble)") // prints: The value is 4.2
}

Update: For localization, it is very easy to apply locales to the NSFormatter as follows:

let formatter = NSNumberFormatter()
formatter.locale = NSLocale(localeIdentifier: "fr_FR")
let double = formatter.numberFromString("100,25")

Finally, you can use NSNumberFormatterCurrencyStyle on the formatter if you are working with currencies where the string contains the currency symbol.


I like the idea of extending String class for apps that will be using the conversion multiple places. But I don't see the benefit of the complexity of invoking NSNumberFormatter, since the cast to NSString and using .doubleValue seems to be at least as direct, and easier to read and remember.
Actually the advantage of using NSNumberFormatter() is that the numberFromString: method will return nil if the string contains characters that don't make up a double. The problem with doubleValue on NSString is that it will always return a double (0.0 for "test" for example) regardless. So if you're dealing with all kinds of strings and you want to test whether the string is just a double, no additional characters, using NSNumberFormatter is the way to go.
This fails on French.
This is a great solution, but using only this code it won't work in countries where commas are used as separators (including France as @PeterK. mentioned). I tried writing a comment with the solution, but it was too long so I had to make it an answer instead. Here you go: Modified solution
K
Keith Smiley

Another option here is converting this to an NSString and using that:

let string = NSString(string: mySwiftString)
string.doubleValue

I wish there was a nicer way to do this. How can I know if parsing failed, for example.
@RyanHoffman Unfortunately the NSString doubleValue contract has never provided any of that. All you get is 0.0 if the string doesn't begin with a valid representation of a number, HUGE_VAL and -HUGE_VAL on overflow/underflow. If you want to know why parsing failed, I believe you need to look into NSNumberFormatter, which was designed to handle number/string conversion in a more robust manner.
Since it may be difficult to find that answer, here it is: (swiftString as NSString).doubleValue
M
Millie Smith

Here's an extension method that allows you to simply call doubleValue() on a Swift string and get a double back (example output comes first)

println("543.29".doubleValue())
println("543".doubleValue())
println(".29".doubleValue())
println("0.29".doubleValue())

println("-543.29".doubleValue())
println("-543".doubleValue())
println("-.29".doubleValue())
println("-0.29".doubleValue())

//prints
543.29
543.0
0.29
0.29
-543.29
-543.0
-0.29
-0.29

Here's the extension method:

extension String {
    func doubleValue() -> Double
    {
        let minusAscii: UInt8 = 45
        let dotAscii: UInt8 = 46
        let zeroAscii: UInt8 = 48

        var res = 0.0
        let ascii = self.utf8

        var whole = [Double]()
        var current = ascii.startIndex

        let negative = current != ascii.endIndex && ascii[current] == minusAscii
        if (negative)
        {
            current = current.successor()
        }

        while current != ascii.endIndex && ascii[current] != dotAscii
        {
            whole.append(Double(ascii[current] - zeroAscii))
            current = current.successor()
        }

        //whole number
        var factor: Double = 1
        for var i = countElements(whole) - 1; i >= 0; i--
        {
            res += Double(whole[i]) * factor
            factor *= 10
        }

        //mantissa
        if current != ascii.endIndex
        {
            factor = 0.1
            current = current.successor()
            while current != ascii.endIndex
            {
                res += Double(ascii[current] - zeroAscii) * factor
                factor *= 0.1
                current = current.successor()
           }
        }

        if (negative)
        {
            res *= -1;
        }

        return res
    }
}

No error checking, but you can add it if you need it.


r
rintaro

As of Swift 1.1, you can directly pass String to const char * parameter.

import Foundation

let str = "123.4567"
let num = atof(str) // -> 123.4567

atof("123.4567fubar") // -> 123.4567

If you don't like deprecated atof:

strtod("765.4321", nil) // -> 765.4321

One caveat: the behavior of conversion is different from NSString.doubleValue.

atof and strtod accept 0x prefixed hex string:

atof("0xffp-2") // -> 63.75
atof("12.3456e+2") // -> 1,234.56
atof("nan") // -> (not a number)
atof("inf") // -> (+infinity)

If you prefer .doubleValue behavior, we can still use CFString bridging:

let str = "0xff"
atof(str)                      // -> 255.0
strtod(str, nil)               // -> 255.0
CFStringGetDoubleValue(str)    // -> 0.0
(str as NSString).doubleValue  // -> 0.0

I'm a fan of this approach instead of casting to NSString.
T
Toom

In Swift 2.0 the best way is to avoid thinking like an Objective-C developer. So you should not "convert a String to a Double" but you should "initialize a Double from a String". Apple doc over here: https://developer.apple.com/library/ios//documentation/Swift/Reference/Swift_Double_Structure/index.html#//apple_ref/swift/structctr/Double/s:FSdcFMSdFSSGSqSd_

It's an optional init so you can use the nil coalescing operator (??) to set a default value. Example:

let myDouble = Double("1.1") ?? 0.0

Double.init?(_ text: String) become available in Swift 2.0. You should mention this. Other answers here is not because people think like Objective-C developers but because this initializer was not available earlier.
Indeed you're right. This is only available in Swift 2.0, I'll mention it :) But like in other languages you initialize a type with another and should not expect a type to be able to convert into another. Long debate but as an Obj-C developer I have many bad practices when coding with Swift and this is one of them :)
T
TomCobo

I haven't seen the answer I was looking for. I just post in here mine in case it can help anyone. This answer is valid only if you don't need a specific format.

Swift 3

extension String {
    var toDouble: Double {
        return Double(self) ?? 0.0
    }
}

J
Jorge Luis Jiménez

On SWIFT 3, you can use:

if let myDouble = NumberFormatter().number(from: yourString)?.doubleValue {
   print("My double: \(myDouble)")
}

Note: - If a string contains any characters other than numerical digits or locale-appropriate group or decimal separators, parsing will fail. - Any leading or trailing space separator characters in a string are ignored. For example, the strings “ 5”, “5 ”, and “5” all produce the number 5.

Taken from the documentation: https://developer.apple.com/reference/foundation/numberformatter/1408845-number


C
CW0007007

Try this:

   var myDouble = myString.bridgeToObjectiveC().doubleValue
   println(myDouble)

NOTE

Removed in Beta 5. This no longer works ?


bridgeToObjectiveC() has been removed in Xcode 6 Beta 5. Mr Smiley's method is the quickest I've found at the moment
i
itsji10dra

This is building upon the answer by @Ryu

His solution is great as long as you're in a country where dots are used as separators. By default NSNumberFormatter uses the devices locale. Therefore this will fail in all countries where a comma is used as the default separator (including France as @PeterK. mentioned) if the number uses dots as separators (which is normally the case). To set the locale of this NSNumberFormatter to be US and thus use dots as separators replace the line

return NSNumberFormatter().numberFromString(self)?.doubleValue

with

let numberFormatter = NSNumberFormatter()
numberFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
return numberFormatter.numberFromString(self)?.doubleValue

Therefore the full code becomes

extension String {
    func toDouble() -> Double? {
        let numberFormatter = NSNumberFormatter()
        numberFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
        return numberFormatter.numberFromString(self)?.doubleValue
    }
}

To use this, just call "Your text goes here".toDouble()

This will return an optional Double?

As @Ryu mentioned you can either force unwrap:

println("The value is \(myDouble!)") // prints: The value is 4.2

or use an if let statement:

if let myDouble = myDouble {
    println("The value is \(myDouble)") // prints: The value is 4.2
}

This is the right answer. The Swift 3 version will be NumberFormatter().number(from: myString)?.doubleValue
t
t4nhpt

SWIFT 4

extension String {
    func toDouble() -> Double? {
        let numberFormatter = NumberFormatter()
        numberFormatter.locale = Locale(identifier: "en_US_POSIX")
        return numberFormatter.number(from: self)?.doubleValue
    }
}

V
Vishal Vaghasiya

Swift 4.0

try this

 let str:String = "111.11"
 let tempString = (str as NSString).doubleValue
 print("String:-",tempString)

A
Abhirajsinh Thakore

Swift : 4 and 5

There are possibly two ways to do this:

String -> Int -> Double: let strNumber = "314" if let intFromString = Int(strNumber){ let dobleFromInt = Double(intFromString) print(dobleFromInt) } String -> NSString -> Double let strNumber1 = "314" let NSstringFromString = NSString(string: strNumber1) let doubleFromNSString = NSstringFromString.doubleValue print(doubleFromNSString)

Use it anyway you like according to you need of the code.


First method doesn't work for me, however the second one works pretty well.
@Saurabh, Can you please elaborate more on What your string was.?
M
Mamazur

Please check it on playground!

let sString = "236.86"

var dNumber = NSNumberFormatter().numberFromString(sString)
var nDouble = dNumber!
var eNumber = Double(nDouble) * 3.7

By the way in my Xcode

.toDouble() - doesn't exist

.doubleValue create value 0.0 from not numerical strings...


F
Firo

As already pointed out, the best way to achieve this is with direct casting:

(myString as NSString).doubleValue

Building from that, you can make a slick native Swift String extension:

extension String {
    var doubleValue: Double {
        return (self as NSString).doubleValue
    }
}

This allows you to directly use:

myString.doubleValue

Which will perform the casting for you. If Apple does add a doubleValue to the native String you just need to remove the extension and the rest of your code will automatically compile fine!


Apple's method would be called .toDouble... same as toInt. It's a strange omission. Likely to be rectified in a future Swift version. I like the extension method the best, keeps the code clean.
The return value should be a Double NOT a Float
D
Daxesh Nagar

1.

let strswift = "12"
let double = (strswift as NSString).doubleValue

2.

var strswift= "10.6"
var double : Double = NSString(string: strswift).doubleValue 

May be this help for you.


i
imike

Extension with optional locale

Swift 2.2

extension String {
    func toDouble(locale: NSLocale? = nil) -> Double? {
        let formatter = NSNumberFormatter()
        if let locale = locale {
            formatter.locale = locale
        }
        return formatter.numberFromString(self)?.doubleValue
    }
}

Swift 3.1

extension String {
    func toDouble(_ locale: Locale) -> Double {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.locale = locale
        formatter.usesGroupingSeparator = true
        if let result = formatter.number(from: self)?.doubleValue {
            return result
        } else {
            return 0
        }
    }
}

I've added Swift 3.1 example. What you mean by "...and add an example for locale"? 🙂
a
aturan23

SWIFT 3

To clear, nowadays there is a default method:

public init?(_ text: String)` of `Double` class.

It can be used for all classes.

let c = Double("-1.0")
let f = Double("0x1c.6")
let i = Double("inf")

, etc.


j
jr00n

Or you could do:

var myDouble = Double((mySwiftString.text as NSString).doubleValue)

S
Stas

You can use StringEx. It extends String with string-to-number conversions including toDouble().

extension String {
    func toDouble() -> Double?
}

It verifies the string and fails if it can't be converted to double.

Example:

import StringEx

let str = "123.45678"
if let num = str.toDouble() {
    println("Number: \(num)")
} else {
    println("Invalid string")
}

A
Aitor Lucas

Swift 4

extension String {
    func toDouble() -> Double {
        let nsString = self as NSString
        return nsString.doubleValue
    }
}

B
Bijan

What also works:

// Init default Double variable
var scanned: Double()

let scanner = NSScanner(string: "String to Scan")
scanner.scanDouble(&scanned)

// scanned has now the scanned value if something was found.

NOTE: 'scanDouble' was deprecated in iOS 13.0
P
Paul B

Using Scanner in some cases is a very convenient way of extracting numbers from a string. And it is almost as powerful as NumberFormatter when it comes to decoding and dealing with different number formats and locales. It can extract numbers and currencies with different decimal and group separators.

import Foundation
// The code below includes manual fix for whitespaces (for French case)
let strings = ["en_US": "My salary is $9,999.99",
               "fr_FR": "Mon salaire est 9 999,99€",
               "de_DE": "Mein Gehalt ist 9999,99€",
               "en_GB": "My salary is £9,999.99" ]
// Just for referce
let allPossibleDecimalSeparators = Set(Locale.availableIdentifiers.compactMap({ Locale(identifier: $0).decimalSeparator}))
print(allPossibleDecimalSeparators)
for str in strings {
    let locale = Locale(identifier: str.key)
    let valStr = str.value.filter{!($0.isWhitespace || $0 == Character(locale.groupingSeparator ?? ""))}
    print("Value String", valStr)

    let sc = Scanner(string: valStr)
    // we could do this more reliably with `filter` as well
    sc.charactersToBeSkipped = CharacterSet.decimalDigits.inverted
    sc.locale = locale

    print("Locale \(locale.identifier) grouping separator: |\(locale.groupingSeparator ?? "")| . Decimal separator: \(locale.decimalSeparator ?? "")")
    while !(sc.isAtEnd) {
        if let val = sc.scanDouble() {
            print(val)
        }

    }
}

However, there are issues with separators that could be conceived as word delimiters.

// This doesn't work. `Scanner` just ignores grouping separators because scanner tends to seek for multiple values
// It just refuses to ignore spaces or commas for example.
let strings = ["en_US": "$9,999.99", "fr_FR": "9999,99€", "de_DE": "9999,99€", "en_GB": "£9,999.99" ]
for str in strings {
    let locale = Locale(identifier: str.key)
    let sc = Scanner(string: str.value)
    sc.charactersToBeSkipped = CharacterSet.decimalDigits.inverted.union(CharacterSet(charactersIn: locale.groupingSeparator ?? ""))
    sc.locale = locale
    print("Locale \(locale.identifier) grouping separator: \(locale.groupingSeparator ?? "") . Decimal separator: \(locale.decimalSeparator ?? "")")
    while !(sc.isAtEnd) {
        if let val = sc.scanDouble() {
            print(val)
        }

    }
}
//     sc.scanDouble(representation: Scanner.NumberRepresentation) could help if there were .currency case

There is no problem to auto detect locale. Note that groupingSeparator in French locale in string "Mon salaire est 9 999,99€" is not a space, though it may render exactly as space (here it doesn't). Thats why the code below works fine without !$0.isWhitespace characters being filtered out.

let stringsArr = ["My salary is $9,999.99",
                  "Mon salaire est 9 999,99€",
                  "Mein Gehalt ist 9.999,99€",
                  "My salary is £9,999.99" ]

let tagger = NSLinguisticTagger(tagSchemes: [.language], options: Int(NSLinguisticTagger.Options.init().rawValue))
for str in stringsArr {
    tagger.string = str
    let locale = Locale(identifier: tagger.dominantLanguage ?? "en")
    let valStr = str.filter{!($0 == Character(locale.groupingSeparator ?? ""))}
    print("Value String", valStr)

    let sc = Scanner(string: valStr)
    // we could do this more reliably with `filter` as well
    sc.charactersToBeSkipped = CharacterSet.decimalDigits.inverted
    sc.locale = locale

    print("Locale \(locale.identifier) grouping separator: |\(locale.groupingSeparator ?? "")| . Decimal separator: \(locale.decimalSeparator ?? "")")
    while !(sc.isAtEnd) {
        if let val = sc.scanDouble() {
            print(val)
        }

    }
}
// Also will fail if groupingSeparator == decimalSeparator (but don't think it's possible)

a
aturan23

Use this code in Swift 2.0

let strWithFloat = "78.65"
let floatFromString = Double(strWithFloat)

S
Stoull

In the cases of strings contain other characters like: "27.8 °C", "52.523553 kM" or "Total: 349.0".

This works in Swift 4:

let anyString = "52.523553 kM"
let digitsCharacterSet = CharacterSet.init(charactersIn: "0123456789.")
let doubleResult = Double(anyString.components(separatedBy:digitsCharacterSet.inverted).joined())

Caution! This not working for strings contain multiple . like "27.8 °C 3.5 kM"


i
itsji10dra

I find more readable to add an extension to String as follow:

extension String {
    var doubleValue: Double {
        return (self as NSString).doubleValue
    }
}

and then you just could write your code:

myDouble = myString.doubleValue

M
Murat Akdeniz

my problem was comma so i solve it this way:

extension String {
    var doubleValue: Double {
        return Double((self.replacingOccurrences(of: ",", with: ".") as NSString).doubleValue)
    }
}

i
itsji10dra
var stringValue = "55"

var convertToDouble = Double((stringValue as NSString).doubleValue)

A
ANIL.MUNDURU

we can use CDouble value which will be obtained by myString.doubleValue


Error: 'String' does not have a member named 'doubleValue'
@Matthew Yes there is no member called "double value", so we can use CDouble Value
en.wikipedia.org/wiki/… Refer to complex numbers there and read the Swift reference documentation.. There isn't any CDouble... Are you you talking about Swift or another language? And I mean swift without extensions?