Unexpected behavior when casting an NSNumber to Float

This is a consequence of SE-0170 NSNumber bridging and Numeric types, implemented in Swift 4:

as? for NSNumber should mean “Can I safely express the value stored in this opaque box called a NSNumber as the value I want?”.

1.12 is a floating point literal, and inferred as a Double, so NSNumber(value: 1.12) is “boxing” the 64-bit floating point value
closest to 1.12. Converting that to a 32-bit Float does not
preserve this value:

let n = NSNumber(value: 1.12)
let x = Float(truncating: n) // Or: let x = n.floatValue
let nn = NSNumber(value: x)
print(n == nn) // false

On the other hand, 1.0 can be represented exactly as a Float:

let m = NSNumber(value: 1.0)
let y = m.floatValue
let mm = NSNumber(value: y)
print(m == mm) // true

and that is why casting m as? Float succeeds. Both

n.floatValue
Float(truncating: n)

can be used to ”truncate” the number to the closest representable
32-bit floating point value.

Leave a Comment