The canonical example used for explaining the binary vs decimal representation of floating points is the value 0.3:
float asFloat = 0.3; // <-- 0.300000012
double asDouble = 0.3; // <-- 0.29999999999999999
double asCastedDouble = static_cast<double>( asFloat ); // <-- 0.30000001192092896
asFloat and asDouble are the closest possible binary representation possible of the literal 0.3. A naïve look might be surprised at the value of asCastedDouble, but then we remember that it’s the closest representation of the float value, not of the literal, so that makes sense. I do wonder, however, why the casted double representation is not identical to the float representation (which is already in binary) with added zero bits in the mantissa, which in decimal would translate to 0.300000011200000000, but that is a separate question.
Consider now a second example for a different literal:
float asFloat2 = -0.488730401; // <-- -0.488730401
double asDouble2 = -0.488730401; // <-- -0.48873040099999998
double asCastedDouble2 = static_cast<double>( asFloat2 ); // <-- -0.48873040080070496
This literal happens to be accurately representable in binary, hence asFloat2 is the same as our literal value. Just like in the last example, I wonder why asDouble2 is “less” accurate than asFloat2 instead of being identical with extra zero bits in the mantissa, but I’ll let it slide.
The question is now: why is asCastedDouble2 the value that it is? It is not the same setup as the previous example, as in there we had a precision error introduced in the float, which was then carried into the double. In this case, however, the float value is identical to the literal, so where does the value of as asCastedDouble2 come from? It is clearly not the closest binary representation possible, since asDouble2 shows us a closer representation?
So I guess my question is twofold:
I do wonder, however, why the casted double representation is not identical to the float representation
They are identical, you just don't print enough digits.
#include <iomanip>
#include <iostream>
int main()
{
float asFloat = 0.3;
double asDouble = 0.3;
double asCastedDouble = static_cast<double>( asFloat );
std::cout << std::setprecision(1000);
std::cout << asFloat << '\n'; // 0.300000011920928955078125
std::cout << asDouble << '\n'; // 0.299999999999999988897769753748434595763683319091796875
std::cout << asCastedDouble << '\n'; // 0.300000011920928955078125
}
Consider now a second example ... This literal happens to be accurately representable in binary
No it isn't. You're not printing enough digits.
#include <iomanip>
#include <iostream>
int main()
{
float asFloat = -0.488730401;
double asDouble = -0.488730401;
double asCastedDouble = static_cast<double>( asFloat );
std::cout << std::setprecision(1000);
std::cout << asFloat << '\n'; // -0.4887304008007049560546875
std::cout << asDouble << '\n'; // -0.4887304009999999809821247254149056971073150634765625
std::cout << asCastedDouble << '\n'; // -0.4887304008007049560546875
}
There are no numbers representable as a float that are not representable as a double.
You are confusing the usual rounded printed output in decimal and the actual number represented inside the machine. To display the 32 bit float "0.3" accurately in decimal form requires a lot of decimal digits. Enough decimal digits to display 1/2^24 exactly.
See for example IEEE 754 Base converter where you can put your chosen number in and see both representations in 32 bit float and 64 bit double (exact decimal, hex and binary).
Float promotion to double faithfully zero pads the mantissa up to 53 bits and extends the range of the exponent from +/-127 to +/-1023.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With