问题
Let's say I have two arithmetic types, an integer one, I
, and a floating point one, F
. I also assume that std::numeric_limits<I>::max()
is smaller than std::numeric_limits<F>::max()
.
Now, let's say I have a positive integer value i
. Because the representable range of F
is larger than I
, F(i)
should always be defined behavior.
However, if I have a floating point value f
such that f == F(i)
, is I(f)
well defined? In other words, is I(F(i))
always defined behavior?
Relevant section from the C++14 standard:
4.9 Floating-integral conversions [conv.fpint]
- A prvalue of a floating point type can be converted to a prvalue of an integer type. The conversion truncates; that is, the fractional part is discarded. The behavior is undefined if the truncated value cannot be represented in the destination type. [ Note: If the destination type is
bool
, see 4.12. — end note ]- A prvalue of an integer type or of an unscoped enumeration type can be converted to a prvalue of a floating point type. The result is exact if possible. If the value being converted is in the range of values that can be represented but the value cannot be represented exactly, it is an implementation-defined choice of either the next lower or higher representable value. [ Note: Loss of precision occurs if the integral value cannot be represented exactly as a value of the floating type. — end note ] If the value being converted is outside the range of values that can be represented, the behavior is undefined. If the source type is
bool
, the valuefalse
is converted to zero and the valuetrue
is converted to one.
回答1:
However, if I have a floating point value
f
such thatf == F(i)
, isI(f)
well defined? In other words, isI(F(i))
always defined behavior?
No.
Suppose that I
is a signed two's complement 32 bit integer type, F
is a 32 bit single precision floating point type, and i
is the maximum positive integer. This is within the range of the floating point type, but it cannot be represented exactly as a floating point number. Some of those 32 bits are used for the exponent.
Instead, the conversion from integer to floating point is implementation dependent, but typically is done by rounding to the closest representable value. That rounded value is one beyond the range of the integer type. The conversion back to integer fails (better said, it's undefined behavior).
回答2:
No.
It's possible that i == std::numeric_limits<I>::max()
, but that i
is not exactly representable in F
.
If the value being converted is in the range of values that can be represented but the value cannot be represented exactly, it is an implementation-defined choice of either the next lower or higher representable value.
Since the next higher representable value may be chosen, it's possible that the result F(i)
no longer fits into I
, so conversion back would be undefined behavior.
回答3:
No. Regardless of the standard, you cannot expect that in general this conversion will return your original integer. It doesn't make sense mathematically. But if you read into what you quoted, the standard clearly indicates the possibility of a loss of precision upon converting from int to float.
Suppose your types I and F use the same number of bits. All of the bits of I (save possibly one that stores the sign) are used to specify the absolute value of the number. On the other hand, in F, some bits are used to specify the exponent and some are used for the significand. The range will be greater because of the possible exponent. But the significand will have less precision because there are fewer bits devoted to its specification.
Just as a test, I printed
std::numeric_limits<int>::max();
std::numeric_limits<float>::max();
I then converted the first number to float and back again. The max float had an exponent of 38, and the max int had 10 digits, so clearly float has a larger range. But upon converting the max int to float and back, I went from 2147473647
to -2147473648
. So it seems the number was incremented by one unit and went around to the negative side.
I didn't check how many bits are actually used for float on my system, but it at least demonstrates the loss of precision, and it shows that gcc "rounded up".
来源:https://stackoverflow.com/questions/29932335/is-round-trip-through-floating-point-always-defined-behavior-if-floating-point-r