I hope this question is not silly. When converting a Float32
value back to Float64
, I get something like this:
julia> Float64(Float32(0.1))
0.10000000149011612
Is there a way to obtain exactly 0.1 without having the nonzero digits coming at the end? Thank you!
Edit:
The purpose is not to print a single value but to do a bunch of computation based on data loaded from files that are in Float32. It’s not something crucial but I am still curious.
This is just how binary floating-point works. What is your actual issue? Do you know about the Printf stdlib, perhaps it would be useful for you.
The issue is that 0.1
is not a number that Float32
(or Float64
or any binary floating point format) can represent. It can only represent numbers that are integer multiples of powers of 2 within certain ranges and with limited precision.
julia> Base.decompose(0.1f0) # Float32(0.1)
(13421773, -27, 1)
julia> 13421773 * 2.0^-27 / 1 # always `/ 1` for Float32, Float64, etc
0.10000000149011612
julia> Float32(13421773 * 2.0^-27 / 1)
0.1f0
julia> BigFloat(0.1e0) # even `Float64` cannot represent 0.1 exactly
0.1000000000000000055511151231257827021181583404541015625
julia> big"0.1" # even `BigFloat` has its limits (adding more precision will reduce but never solve the problem)
0.1000000000000000000000000000000000000000000000000000000000000000000000000000002
So what the computer says is 0.1f0
is actually 13421773 * 2^{-27} = \frac{13421773}{134217728}, which is equal to the number you see when you add extra bits for Float64
.
In essence, it is adding zeros to the end of the Float32
. But those zeros are added to the binary representation, rather than decimal:
julia> bitstring(0.1f0)
"00111101110011001100110011001101"
# ^ start comparing here and to the right
julia> bitstring(Float64(0.1f0))
"0011111110111001100110011001100110100000000000000000000000000000"
# ^ start comparing here and to the right
julia> Int(0b110011001100110011001101)
13421773
# ^ must insert a leading 1 because that's what the these float representations do
(The bits to the left are for the sign and exponent. The exponent bits will be different because the different types use different offsets for their exponents. See Wikipedia for more info.)
So the number it’s giving you is correct and is the number you were always using. It’s just that the Float32
stops printing early because there is no other Float32
that would round that way. Float64
has more precision so can distinguish between more values, so you see the extra digits.
8 Likes