Hey There!  First of all I wanted to start by saying I read a lot of your stuff and am very appreciative of the time you take in writing down your thoughts, books and answering user emails.

I wanted to share with you a very interesting gotcha I stumbled upon today while working with Float currencies. Apparently when a Float is wrapped in an Optional, it loses its precision.  Here’s a screenshot to show what I’m talking about 🙂

This isn’t a huge deal since unwrapping solves the problem, But I thought if someone would like an odd swift-WAT moment like this, It’d be you.  What do you think? Ever stumbled upon something of sorts?

Many thanks, Shai

I don’t believe this is a gotcha. I think what you’re seeing is that Optionals use different code to render their output descriptions than Floats. I created a workaround, submitted a bug report, and will explain here what’s actually going on:

To test whether an amount is “losing precision”, I started with your code:

```let amount: Float? = 3.8
print(amount)
print(amount!)```

And then I declared a non-optional amount:

`let nonoptAmount: Float = 3.8`

Floats are 4 bytes long. You can test this for the moment using sizeof(Float.self). (I have a proposal in that changes this call.) Your hypothesis states that unwrapping the value loses precision. So I performed an unsafe bitcast on both the unwrapped and the unwrapped amounts to UInt32, which is also 4 bytes long.

```public func --><T, U>(value: T, target: U.Type) -> U? {
guard sizeof(T.self) == sizeof(U.self) else { return nil }
return unsafeBitCast(value, to: target)
}

let optbits = (amount! --> UInt32.self)!
let nonoptbits = (nonoptAmount --> UInt32.self)!```

If the unwrapping has truly lost precision the two UInt32 values will have different bit representations, but they don’t:

```print(String(optbits, radix: 2))

Then I pushed the bits from the UInt32 back to a float and wrapped them in an optional, to make sure the results were the same. They were:

What’s actually going wrong is that Optional does not conform to CustomStringConvertible, so when it prints it’s not leveraging the wrapped value’s preferred output style — assuming the wrapped value itself conforms to CustomStringConvertible.

So I went ahead and implemented an extension to let Optional take advantage of this protocol. Doing so, immediately changed the results of your print(amount)/print(amount!) output:

Bug Report SR-2062

Update: Via Joe Groff at Swift JIRA:

 This behavior is intentional. Optional does conform to CustomDebugStringConvertible.  We wanted to make it clear you’re printing a wrapped Optional so that in playgrounds or printf-debugging scenarios, it’s clear the value is wrapped.

• This is very clever stuff, a great article as always!

• Awesome to see why this was happening. Just to remind people, there is nothing “magical” about optionals except perhaps the syntactic sugar around them. An optional is just a generic enum with a .None and .Some case:

public enum Optional : _Reflectable, NilLiteralConvertible {
case None
case Some(Wrapped)

}

Simple as that!

• Thanks for the write up ! 🙂

• FYI you should not use `Float` for currencies you should use the type `Decimal`

let amount: Decimal? = Decimal(3.8)

print(amount) // “Optional(3.8)\n”
print(amount!) // “3.8\n”

• Very nice. Why not `terminator:”)”`? It would avoid one copy, right?