Double, Floating-Point Numbers in Swift

Swift Language

Double-precision, Floating-point numbers are numbers that have fractional parts (usually expressed with a decimal point). You should use a floating-point type in Swift programs whenever you need a number with a decimal, such as 24.02 or 3.25.

Floating-point types can represent a much wider range of values than integer types, and can store numbers that are much larger or smaller than can be stored in an Int. Swift provides two signed floating-point number types:

  • Double represents a 64-bit floating-point number.
  • Float represents a 32-bit floating-point number.

1. Converting Integers to Double

If two representable values are equally close, the result is the value with more trailing zeros in its significand bit pattern.

2. Converting Strings to Double

The input string to convert to a Double instance. If text has invalid characters or is in an invalid format, the result is nil. The string passed as text can represent a real number in decimal or hexadecimal format or special floating-point values for infinity and NaN (“not a number”).

The given string may begin with a plus or minus sign character (+ or -). The allowed formats for each of these representations is then as follows:

2.1. A decimal value contains the significand, a sequence of decimal digits that may include a decimal point.

let c = Double("-1.0")
// c == -1.0

let d = Double("28.375")
// d == 28.375

A decimal value may also include an exponent following the significand, indicating the power of 10 by which the significand should be multiplied. If included, the exponent is separated by a single character, e or E, and consists of an optional plus or minus sign character and a sequence of decimal digits.

let e = Double("2837.5e-2")
// e == 28.375

2.2. A hexadecimal value contains the significand, either 0X or 0x, followed by a sequence of hexadecimal digits. The significand may include a decimal point.

let f = Double("0x1c.6")
// f == 28.375

A hexadecimal value may also include an exponent following the significand, indicating the power of 2 by which the significand should be multiplied. If included, the exponent is separated by a single character, p or P, and consists of an optional plus or minus sign character and a sequence of decimal digits.

let g = Double("0x1.c6p4")
// g == 28.375

2.3. A value of infinity contains one of the strings “inf” or “infinity”, case insensitive.

let i = Double("inf")
// i == Double.infinity

let j = Double("-Infinity")
// j == -Double.infinity

2.4. A value of NaN contains the string “nan”, case insensitive.

let n = Double("-nan")
// n?.isNaN == true
// n?.sign == .minus

A NaN value may also include a payload in parentheses following the “nan” keyword. The payload consists of a sequence of decimal digits, or the characters 0X or 0x followed by a sequence of hexadecimal digits. If the payload contains any other characters, it is ignored. If the value of the payload is larger than can be stored as the payload of a Double.nan, the least significant bits are used.

let p = Double("nan(0x10)")
// p?.isNaN == true
// String(p!) == "nan(0x10)"

Passing any other format or any additional characters as text results in nil. For example, the following conversions result in nil:

Double(" 5.0")      // Includes whitespace
Double("±2.0")      // Invalid character
Double("0x1.25e4")  // Incorrect exponent format

3. Converting Floating-Point Values

3.1. Creates a new instance initialized to the given value

The value of other is represented exactly by the new instance. A NaN passed as otherresults in another NaN, with a signaling NaN value converted to quiet NaN.

let x: Double = 21.25
let y = Double(x)
// y == 21.25

let z = Double(Double.nan)
// z.isNaN == true

3.2. Creates a new instance that approximates the given value

The value of other is rounded to a representable value, if necessary. A NaN passed as otherresults in another NaN, with a signaling NaN value converted to quiet NaN.

With Float:

let x: Float = 21.25
let y = Double(x)
// y == 21.25

let z = Double(Float.nan)
// z.isNaN == true

With Float80:

let x: Float80 = 21.25
let y = Double(x)
// y == 21.25

let z = Double(Float80.nan)
// z.isNaN == true

3.3. Creates a new value from the given sign, exponent, and significand

The following example uses this initializer to create a new Double instance. Double is a binary floating-point type that has a radix of 2.

let x = Double(sign: .plus, exponent: -2, significand: 1.5)
// x == 0.375

This initializer is equivalent to the following calculation, where ** is exponentiation, computed as if by a single, correctly rounded, floating-point operation:

let sign: FloatingPointSign = .plus
let exponent = -2
let significand = 1.5
let y = (sign == .minus ? -1 : 1) * significand * Double.radix ** exponent
// y == 0.375

As with any basic operation, if this value is outside the representable range of the type, overflow or underflow occurs, and zero, a subnormal value, or infinity may result. In addition, there are two other edge cases:

  • If the value you pass to significand is zero or infinite, the result is zero or infinite, regardless of the value of exponent.
  • If the value you pass to significand is NaN, the result is NaN.

For any floating-point value x of type F, the result of the following is equal to x, with the distinction that the result is canonicalized if x is in a noncanonical encoding:

let x0 = F(sign: x.sign, exponent: x.exponent, significand: x.significand)

This initializer implements the scaleB operation defined by the IEEE 754 specification.

4. Converting with No Loss of Precision

4.1. Creates a new instance initialized to the given value, if it can be represented without rounding

If other can’t be represented as an instance of Double without rounding, the result of this initializer is nil. In particular, passing NaN as other always results in nil.

let x: Float80 = 21.25
let y = Double(exactly: x)
// y == Optional.some(21.25)

let z = Double(exactly: Float80.nan)
// z == nil

4.2. Creates a new instance initialized to the given value, if it can be represented without rounding

let x: Double = 21.25
let y = Double(exactly: x)
// y == Optional.some(21.25)

let z = Double(exactly: Double.nan)
// z == nil

5. Creating a Random Value

5.1. Returns a random value within the specified range

Use this method to generate a floating-point value within a specific range. This example creates three new values in the range 10.0 ..< 20.0.

for _ in 1...3 {
    print(Double.random(in: 10.0 ..< 20.0))
}
// Prints "18.1900709259179"
// Prints "14.2286325689993"
// Prints "13.1485686260762"

The random() static method chooses a random value from a continuous uniform distribution in range, and then converts that value to the nearest representable value in this type. Depending on the size and span of range, some concrete values may be represented more frequently than others.

This method is equivalent to calling random(in:using:), passing in the system’s default random generator.

5.2. Returns a random value within the specified range, using the given generator as a source for randomness

Use this method to generate a floating-point value within a specific range when you are using a custom random number generator. This example creates three new values in the range 10.0 ..< 20.0

for _ in 1...3 {
    print(Double.random(in: 10.0 ..< 20.0, using: &myGenerator))
}
// Prints "18.1900709259179"
// Prints "14.2286325689993"
// Prints "13.1485686260762"

The random(in:using:) static method chooses a random value from a continuous uniform distribution in range, and then converts that value to the nearest representable value in this type. Depending on the size and span of range, some concrete values may be represented more frequently than others.

5.3. Returns a random value within the specified range

Use this method to generate a floating-point value within a specific range. This example creates three new values in the range 10.0 ... 20.0

for _ in 1...3 {
    print(Double.random(in: 10.0 ... 20.0))
}
// Prints "18.1900709259179"
// Prints "14.2286325689993"
// Prints "13.1485686260762"

The random() static method chooses a random value from a continuous uniform distribution in range, and then converts that value to the nearest representable value in this type. Depending on the size and span of range, some concrete values may be represented more frequently than others.

This method is equivalent to calling random(in:using:), passing in the system’s default random generator.

5.4. Returns a random value within the specified range, using the given generator as a source for randomness

Use this method to generate a floating-point value within a specific range when you are using a custom random number generator. This example creates three new values in the range 10.0 ... 20.0

for _ in 1...3 {
    print(Double.random(in: 10.0 ... 20.0, using: &myGenerator))
}
// Prints "18.1900709259179"
// Prints "14.2286325689993"
// Prints "13.1485686260762"

The random(in:using:) static method chooses a random value from a continuous uniform distribution in range, and then converts that value to the nearest representable value in this type. Depending on the size and span of range, some concrete values may be represented more frequently than others.