If we have a double-precision value in C++ and do a static_cast<float>
on it, will the returned value always be smaller in absolute value? My intuition behind this says yes for the following reasons.
I have experimented some numerically with this in the following program. It appears that sometimes, rounding up happens, and other times, round down.
Where can I find more info about how I can expect this rounding to behave? Does it always round to the nearest float?
#include <cmath>
#include <iostream>
int main() {
// Start testing double precision values starting at x, going up to max
double x = 0.98;
constexpr double max = 1e10;
// Loop over many possible double-precision values, print out
// if casting to float ever produced a larger number.
int output_counter = 0; // output every n steps
constexpr int output_interval = 100000000;
std::cout.precision(17);
while (x < max) {
// volatile to ensure compiler doesn't optimize this out
volatile float xprime = static_cast<float>(x);
double xprimeprime = static_cast<double>(xprime);
if (xprimeprime > x)
std::cout << "Found a round up! x=" << x << ", xprime = "<< xprime << std::endl;
// Go to the next higher double precision value
x = std::nextafter(x, std::numeric_limits<double>::infinity());
output_counter++;
if (output_counter == output_interval) {
std::cout << x << std::endl;
output_counter = 0;
}
}
}
The standard says in [conv.double]:
A prvalue of floating-point type can be converted to a prvalue of another floating-point type. If the source value can be exactly represented in the destination type, the result of the conversion is that exact representation. If the source value is between two adjacent destination values, the result of the conversion is an implementation-defined choice of either of those values. Otherwise, the behavior is undefined.
Note that with the <limits>
header you can check the round style by std::numeric_limits<T>::round_style
. See [round.style] for the possible values. (At least I assume that floating-point conversion falls under floating-point arithmetic.)
I can't find a definitive answer in the Draft C++17 Standard I normally use for answers to questions such as this1; however, cppreference (which is generally reliable) strongly suggests that the rounding mode for floating-point conversions is implementation defined.
However, it also states that, if IEEE-754 rules are followed, rounding takes places to the nearest representable value2:
Floating-point conversions
A prvalue of a floating-point type can be converted to a prvalue of any other floating-point type. If the conversion is listed under floating-point promotions, it is a promotion and not a conversion.
- If the source value can be represented exactly in the destination type, it does not change. If the source value is between two representable values of the destination type, the result is one of those two values (it is implementation-defined which one, although if IEEE arithmetic is supported, rounding defaults to nearest).
- Otherwise, the behavior is undefined.
Further, that IEE-754 default behaviour referred can be changed, using the std::fesetround(int round)
function, with one of the following rounding modes, defined in the <cfenv>
header:
#define FE_DOWNWARD /*implementation defined*/ // (since C++11)
#define FE_TONEAREST /*implementation defined*/ // (since C++11)
#define FE_TOWARDZERO /*implementation defined*/ // (since C++11)
#define FE_UPWARD /*implementation defined*/ // (since C++11)
1BlameTheBits found the relevant section in the Standard. In the C++17 Draft I referred to, this is actually §7.9.1 but otherwise similar.
2 IEEE-754 actually defines 5 different rules for floating point rounding.
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