How to print floating point numbers in Embedded Swift?

I cannot print or use any String formatting in Embedded Swift (Foundation). It doesn't look like it is supported in this Embedded Swift Status:

How can I print values of floating point numbers?

Is there a workaround API that is supported from C++ or C if I cannot use Swift print or String methods to create strings?

Swift Errors:

1. Conformance of Double to CustomStringConvertible is unavailable in embedded Swift

print(3.14)

Errors with:

 16 |   print(3.14)
    |   `- error: conformance of 'Double' to 'CustomStringConvertible' is unavailable: unavailable in embedded Swift
 17 |   print("\(3.14)")

2. Floating Point String Interpolation Does Not Work

print("\(3.14)")

Results in:

(cannot print value in embedded Swift)

3. printf is unavailable (Variadic function is unavailable)

If I try printf()

error: 'printf' is unavailable: Variadic function is unavailable
 18 |   printf("%f", 3.14)
    |   `- error: 'printf' is unavailable: Variadic function is unavailable

It's a hack, but could work for a fixed number of fractional digits? :see_no_evil:

func format(double: Double) -> String {
    let int = Int(double)
    let frac = Int((double - Double(int)) * 100)
    return "\(int).\(frac)"
}

For arbitrary number of digits you'd need powl, which I assume is also unavailable?

This is most probably ineffecient if your target CPU doesn't have hardware FP support, but in that case you might consider fixed point arithmetic anyway.

1 Like

Great idea, I was trying to import a C/C++ function wrapper around printf(), but I couldn't get CMake to build it properly with the BridgingHeader.h.

This function allows me to inspect the floating point calculations, which are different than what I expect from my Xcode sample project.

You could use the bitPattern (binary interchange format).

Embedded Swift:

let x: Float64 = .pi
print(x.bitPattern)
// prints "4614256656552045848"

Regular Swift:

let y = Float64(bitPattern: 4614256656552045848)
print(y)
// prints "3.141592653589793"

I think this is fundamentally just not done yet; I don't think that there's any real reason why we can't print Floating-point numbers in embedded. (cc @kubamracek, @tbkka)

2 Likes

Getting Swift's current implementation to work would "just" require enabling that particular subset of the Swift runtime library.

In theory, you could make it smaller/simpler by adjusting the implementation to be a wrapper around the platform sprintf(). That won't give you the same results as regular Swift, though, since Swift prints floating-point values in shortest round-trip-correct form, which the Standard C library does not support.

For accurate output, you could implement a simple hex-format printer pretty easily. That requires decomposing the original value into a sign, significand, and exponent and then formatting the pieces appropriately. That gives you the exact result in a form that you could easily compare across implementations.

Crappy decimal output can be done by first scaling (multiply or divide by 10 repeatedly) until the value is between 0.1 and 1.0, then repeatedly multiply by 10 to get each successive digit. Repeated multiply or divide can accumulate some nasty rounding errors, so I don't recommend this if you care about the results, but might suffice for non-critical uses.

sprintf is a heavier dependency than SwiftDtoA is, in general. That, plus getting the wrong answer on a lot of platforms, would make it a dealbreaker, I think.

1 Like