Reputation: 597076
I just stumbled upon the following issue. And while I know that floats and doubles have their quirks, this sounds bizarre. Can someone explain (Java 11)?
System.out.println(Float.valueOf(Float.parseFloat("76028223")).intValue());
System.out.println(Double.valueOf(Double.parseDouble("76028223")).intValue());
Result:
76028224
76028223
Upvotes: 1
Views: 1090
Reputation: 41281
A float is stored using an exponent-mantissa representation (similar to scientific notation taught in grade school). The number of bits available to store the mantissa and exponent depends on the specific type (float vs double in Java, as well as more unusual types listed here and elsewhere).
One way to represent the numerical value 76028223 (not referring to any specific floating-point type yet) is 1.001000100000011001001111112 * 2^26.
The mantissa of a float
represents 24 bits (23 stored in the float, one implicit1), meaning that it cannot represent all significant bits of 1.00100010000001100100111111. That mantissa would require 27 bits to represent exactly.
Rather, the best we can do with our float is use the closest 24-bit (1.23 bits) mantissa, which is 1.00100010000001100101000 (obtained by rounding the previous mantissa). This mantissa, combined with the correct exponent, represents 1.001000100000011001010002 * 2^26 = 76028224.
This kind of truncation leads to a number of unexpected surprises, for example 76028223f + 1 == 76028223f
is true (the closest representation to that float literal is 76028224, and the closest float representation of 76028224+1 is also 76028224).
A double
has more precision and hence both a larger exponent range + higher precision mantissa (53 bits of which 52 are explicitly stored), so it can represent your selected input precisely.
1 The leading bit before the "decimal point" is always 1 for a non-subnormal float, so it does not need to be stored.
Upvotes: 10