Looking at the docs for Strings, String | Apple Developer Documentation , i could not find the initializer that takes a double to convert. I wanted to know if the doc is missing an api, because I know there is one..
I’m looking to learn how the conversion is implemented. particularly, what happens if the input double is really long like 0.49999999... For example, in java it returns a string of a value with the fewest decimal places that can convert back to the given double, so possibly 0.45.
To understand how Swift converts types to String you should read up on the initializers under String.init(describing:)
This means that you should investigate each protocol conformance which has a supported initializer via this group.
xwu
(Xiaodi Wu)
November 20, 2025, 9:29pm
3
The relevant initializer is this one, unless I'm mistaken and there's another more specific:
The documentation doesn't have any information specific to floating-point values. However, conformance to LosslessStringConvertible (as all of the standard library floating-point types do) provides an important guarantee that you touch on: the string representation you get has enough information that it can be converted back to exactly the same floating-point value you start out with.
How exactly it's done is an implementation detail which can change at any time. However, since Swift 4.2, the standard library has used a variation of the Grisu2 algorithm with changes described in Errol3, which means that you'll get a string representation with the minimum number of digits required for lossless conversion and with good performance (but, again, the API makes no such guarantee ):
master ← tbkka:tbkka-floating-point-printing-C
opened 01:39AM - 24 Mar 18 UTC
This replaces the current implementation of `description` and
`debugDescription… ` for the standard floating-point types with a new
formatting routine that provides both improved performance and
better results.
Unlike the earlier code based on `sprintf` with a fixed number of
digits, this logic automatically chooses the optimal number of digits,
generating compact output where possible while ensuring round-trip
accuracy. As such, we can now use the exact same output for both `description` and
`debugDescription` (except of course that `debugDescription` provides
full detail for NaNs).
This resolves [SR-106](https://bugs.swift.org/browse/SR-106), [SR-454](https://bugs.swift.org/browse/SR-454), [SR-491](https://bugs.swift.org/browse/SR-491), [SR-3131](https://bugs.swift.org/browse/SR-3131) and other related issues that have complained
about the floating-point `description` and `debugDescription` properties being inexact
and/or inconsistent.
## Ergonomics
With the new code, the REPL generally prints the values you would expect.
```
(swift) 1.1
// r0 : Double = 1.1
(swift) (1.1).description
// r1 : String = "1.1"
(swift) 1e23
// r2 : Double = 1e+23
(swift) print("\(1e23.nextDown) \(1e23) \(1e23.nextUp)")
9.999999999999997e+22 1e+23 1.0000000000000001e+23
(swift) 1.100000000000001
// r3 : Double = 1.100000000000001
(swift) print("\(1.100000000000001)")
1.100000000000001
(swift) 1.0 / 10.0
// r4 : Double = 0.1
(swift) 1.0 / 10.0 + 1.0
// r5 : Double = 1.1
```
In comparison, the previous implementation routinely prints extraneous digits for `debugDescription` (used by the REPL) and omits significant digits in `description` (used by `print`):
```
Welcome to Apple Swift version 4.1 (swiftlang-902.0.38 clang-902.0.31). Type :help for assistance.
1> 1.1
$R0: Double = 1.1000000000000001
2> (1.1).description
$R1: String = "1.1"
3> 1e23
$R2: Double = 9.9999999999999991E+22
4> print("\(1e23.nextDown) \(1e23) \(1e23.nextUp)")
1e+23 1e+23 1e+23
5> 1.100000000000001
$R3: Double = 1.100000000000001
6> print("\(1.100000000000001)")
1.1
7> 1.0 / 10.0
$R4: Double = 0.10000000000000001
8> 1.0 / 10.0 + 1.0
$R5: Double = 1.1000000000000001
```
Of course, this only changes how the floating-point numbers are printed. The actual parsing, storage, and arithmetic operations are unaffected and are still subject to the same rounding issues common to all floating-point arithmetic.
## About the Algorithm
The `SwiftDtoa.c` file here implements a variation of Florian Loitsch' Grisu2
algorithm with changes suggested by Andrysco, Jhala, and Lerner's 2016
paper describing Errol3.
The implementation is:
* Fast. It uses only fixed-width integer arithmetic and has constant
memory and time requirements.
* Simple. It is only a little more complex than Loitsch' original
implementation of Grisu2. The digit decomposition logic for double is
less than 300 lines of standard C (half of which is common arithmetic
support routines).
* Always Accurate. Converting the decimal form back to binary using an
accurate algorithm (such as Clinger's algorithm) will always yield exactly the
original binary value. For the IEEE 754 formats, the round-trip will
produce exactly the same bit pattern in memory. This is an essential
requirement for debugging, logging, and JSON serialization.
* Always Short. This always selects an accurate result with the minimum
number of decimal digits. (So that `1.0 / 10.0` will always print `0.1`.)
* Always Close. Among all accurate, short results, this always chooses
the result that is closest to the exact floating-point value. (In case
of an exact tie, it rounds the last digit even.)
## Performance
The graph below compares the new code (in green) to the performance of three other
popular algorithms. Note that this graph benchmarks just the underlying C code; the Swift
`description` logic must spend additional effort to allocate a returned String.
* Dragon4 (in yellow) uses variable-length arithmetic in order to provide accurate results regardless of the number of digits requested. This causes it to become significantly slower when the input has a large positive or negative exponent. (The other algorithms here do not offer arbitrary numbers of digits, so can use much faster fixed-width arithmetic.) This is the algorithm commonly used by C standard library implementations of the `printf` family of functions.
* Grisu3 + Dragon4 (in red) uses fast fixed-width arithmetic for 99% of values and falls back to Dragon4 about 1% of the time. Note the upper whisker that goes to the same height as Dragon4.
* Errol4 (in blue) is a recent algorithm that uses multiple-precision floating-point arithmetic internally. It is the successor to Errol3 which partly inspired the new Swift implementation.
* The new `SwiftDtoa` implementation (in green) is similar to the Errol algorithms but uses fixed-width integer arithmetic throughout. This gives it uniform fast performance regardless of the input value.
<img width="619" alt="screen shot 2018-03-23 at 5 53 13 pm" src="https://user-images.githubusercontent.com/21696764/37858541-2b83677c-2ec3-11e8-9c5e-9a7dda50e84f.png">
The implementation was recently re-written in native Swift, partly to support Embedded use cases:
main ← tbkka:tbkka-swift-floatingpointtostring
opened 10:38PM - 02 Jul 25 UTC
This replaces the previous SwiftDtoa.cpp with the same algorithm reimplemented i… n Swift. It supports Float16, Float32, Float64, and Float80 (on Intel).
Performance is reasonable: In my testing (M1 and x86_64): Float16 and Float32 are a bit faster than the C version, Float64 is almost exactly the same, Float80 is a bit slower.
I think I've finally worked out the availability, though I'd appreciate someone who knows better taking a critical look.
3 Likes