When you write:
print("\(floatValue)")
or
print(floatValue)
It will end up using floatValue.description which is the shortest textual decimal representation of the value that will convert back to the exact same floating point value (see LosslessStringConvertible).
So let's say we have these:
let v1: Float = 492612.6
let v0 = v1.nextDown // The closest representable value less than v1
let v2 = v1.nextUp // The closest representable value greater than v1
Note that the value of v1 is not exactly 492612.6, it's just the closest representable floating point value of that value. Here's what their bit patterns (sign bit, exponent bits and significand bits) look like:
v0.bitPattern == 0b0_10010001_11100001000100010010010
v1.bitPattern == 0b0_10010001_11100001000100010010011
v2.bitPattern == 0b0_10010001_11100001000100010010100
When we print v0, v1, v2 (which ends up using their .description property), we get the shortest textual decimal representation that can be used to convert back to the exact same underlaying floating point values:
print(v0) // 492612.56
print(v1) // 492612.6
print(v2) // 492612.62
print(v0 == Float("492612.56")) // true
print(v1 == Float("492612.6")) // true
print(v2 == Float("492612.62")) // true
Again, note that the decimal representations we see here are just the shortest ones that will map back to the same underlying floating point values, and they are not necessarily exact:
print(v1 == Float("492612.59")) // true
print(492612.6 as Float == 492612.59 as Float) // true
Since every (finite) single precision IEEE 754 floating point value can be represented exactly with a finite number of decimal digits, we can see what they are:
print(String(format: "%.10f", v0)) // 492612.5625000000
print(String(format: "%.10f", v1)) // 492612.5937500000
print(String(format: "%.10f", v2)) // 492612.6250000000
So, 492612.6 is not representable exactly as a Float value, and the closest representable Float value is exactly 492612.59375.
Note that there would be little point in writing this Float value in decimal as
492612.59 or
492612.594 or
492612.5938 instead of just
492612.6
since they all map to the same closest representable Float value, because the distance between representable values at this magnitude is
0.03125 == Float(492612.6).ulp.
And here's what v1 = Float(492612.6) looks like when printed with 11 to 1 significant decimal digits using .localizedStringWithFormat on my system:
for p in (1 ... 11).reversed() {
print(String.localizedStringWithFormat("%.\(p)g", v1))
}
492 612,59375
492 612,5938
492 612,594
492 612,59
492 612,6
492 613
4,9261×10^+05
4,926×10^+05
4,93×10^+05
4,9×10^+05
5×10^+05
(Format specifiers (like "%f", "%g" and "%.3g") are described in the IEEE printf specification.)