Java’s default formatting produces just enough decimal digits to uniquely distinguish the floating-point number from neighboring floating-point numbers.
When the source text 0.3 is converted to double, the result is the closest double value, which is 0.299999999999999988897769753748434595763683319091796875. When this is printed, “0.3” suffices to uniquely identify it, because 0.299999999999999988897769753748434595763683319091796875 is of course the closest value, since that is how we got it from 0.3 in the first place.
The source texts 0.1 and 0.2 produce 0.1000000000000000055511151231257827021181583404541015625 and
0.200000000000000011102230246251565404236316680908203125. When these are added in double format, the result is 0.3000000000000000444089209850062616169452667236328125. Note this is different from the number above. So, when it is printed, “0.3” does not suffice to distinguish it from the neighboring value 0.299999999999999988897769753748434595763683319091796875. It is necessary to produce “0.30000000000000004” to show that it is different.
When the value is converted to float, formatting for float is used, instead of formatting for double. When formatting for float, only values representable in float are candidates for what are neighbors. The value representable in float that is nearest 0.3 is 0.300000011920928955078125. This is also the result of (float) 0.1 + (float) 0.2, and it is sufficiently distinguished from the neighboring float values (0.2999999821186065673828125 and 0.3000000417232513427734375) by printing merely “0.3”.