From binary to decimal values in Swift (updated Swift 3, Xcode 8)

Before we start it definitely needs to be noted that Swift has binary literals:
0b11011111 // 223
And also that you can convert a binary String to a decimal using the C standard library:
strtoul("11011111", nil, 2) // 223

Bool array as a binary representation

But why should this stop the fun. So first let's begin with a Bool Array, which I've typealiased to Bit for clarity. I begin with Bool because it prevents anything but 0 and 1 (or rather true and false) being used.
typealias Bit = Bool
let B1 = true
let B0 = false

func binaryToDecimal(binary:[Bit]) -> Int {
    var bitValue = 1, total = 0
    for b in binary.reverse() {
        if b == true {
            total += bitValue
        }
        bitValue *= 2
    }
    return total
}

binaryToDecimal([B1,B1,B0,B1,B1,B1,B1,B1]) // 223

Swift 3, Xcode 8

typealias Bit = Bool
let B1 = true
let B0 = false

func binaryToDecimal(binary:[Bit]) -> Int {
    var bitValue = 1, total = 0
    for b in binary.reversed() {
        if b == true {
            total += bitValue
        }
        bitValue *= 2
    }
    return total
}

binaryToDecimal(binary: [B1,B1,B0,B1,B1,B1,B1,B1]) // 223
An enum could substitute the alias but this looks nicer when implemented.

Binary string

If we had a binary string instead then we'd probably end with something like this:
enum BinaryStringError:ErrorType {
    case InvalidCharacter
}
func binaryToDecimal(str:String) throws -> Int {
    var bitValue = 1, total = 0
    for b in str.characters.reverse() {
        if b == "1" {
            total += bitValue
        }
        else if b != "0" { throw BinaryStringError.InvalidCharacter }
        bitValue *= 2
    }
    return total
}

do {
 try binaryToDecimal("11011111") // 223
}
catch BinaryStringError.InvalidCharacter {
    print("not a valid binary string")
}

Swift 3, Xcode 8

enum BinaryStringError:Error {
    case InvalidCharacter
}
func binaryToDecimal(str:String) throws -> Int {
    var bitValue = 1, total = 0
    for b in str.characters.reversed() {
        if b == "1" {
            total += bitValue
        }
        else if b != "0" { throw BinaryStringError.InvalidCharacter }
        bitValue *= 2
    }
    return total
}

do {
    try binaryToDecimal(str: "11011111") // 223
}
catch BinaryStringError.InvalidCharacter {
    print("not a valid binary string")
}
Now we are having to handle errors, or alternatively we'd need some quiet fail.

(Note: a similar approach to my string handling function, but one using pattern matching can be found here.)

A note on C

It's worth noting here that strtoul() treats any character other than 1 as zero. This isn't necessarily what you'd expect if you're used to working with Bool() where any number higher than 0 will return true. So you need to account for this, most likely searching the string for values other than 1 or 0 beforehand. Something like this:
if let _ = str.rangeOfString("[^0^1]", options: .RegularExpressionSearch) { throw BinaryStringError.InvalidCharacter }
which can either throw (as shown here) or return nil depending on the demands of the app.

Swift 3, Xcode 8

enum BinaryStringError:ErrorType {
    case InvalidCharacter
}
func binaryToDecimal(str:String)throws -> UInt {
    if let _ = str.range(of: "[^0^1]", options: .regularExpression) {
        throw BinaryStringError.InvalidCharacter
    }
    else {
        return strtoul("11011111", nil, 2) // 223
    }
}
do {
    try binaryToDecimal(str: str)
}



Comments