Reputation: 1
While doing a conversion test I encounter some strange behavior in C++.
The online C++ reference indicates that the return value of std::numeric_limits<double>::max()
(defined in limit.h
) should be DBL_MAX
(defined in float.h
). In my test, when I print these values out, both are indeed exactly the same. However, when I cast them from double
to int
, strange things came out.
int32_t t1 = (int) std::numeric_limits<double>::max();
sets t1
to INT_MIN
, but int32_t t2 = (int) DBL_MAX;
sets t2
to INT_MAX
. The same is true when the cast is done using static_cast<int>
.
However, if I define a function
int32_t doubleToInt(double dvalue) {
return (int) value;
}
both doubleToInt(std::numeric_limits<double>::max())
and doubleToInt(DBL_MAX)
return INT_MIN
.
To help make sense of things, I implemented a similar program in Java. There, all casts returned the value of INT_MAX
, regardless of being in a function or not.
Can someone point out the reason why in C++ the result is INT_MIN
in some cases, and INT_MAX
in the others? What should the expected behaviour be like when casting DBL_MAX
to int
in C++?
#include <iostream>
#include <limits>
#include <float.h>
#include <stdlib.h>
#include <stdio.h>
using namespace std;
template <typename T, typename D> D cast(T a, D b) { return (D) a;}
int main()
{
int32_t t1 = 9;
std::cout << std::numeric_limits<double>::max() << std::endl;
std::cout << DBL_MAX << std::endl;
std::cout << (int32_t) std::numeric_limits<double>::max() << std::endl;
std::cout << (int32_t) DBL_MAX << std::endl;
std::cout << cast(std::numeric_limits<double>::max(), t1) << std::endl;
std::cout << cast(DBL_MAX, t1) << std::endl;
return 0;
}
For completeness: I am using cygwin gcc and java 8.
Upvotes: 0
Views: 962
Reputation: 241851
Attempting to convert a floating point number greater than INT_MAX
to an int
is undefined behaviour:
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. (§4.9 [conv.fpint], para. 1)
So a compiler can produce any value (or even do something else, like throw an exception) for the conversion. Different compilers can do different things. The same compiler can do different things at different times.
There is no real point attempting to understand why a particular instance of undefined behaviour shows the result it shows (unless you are trying to reverse engineer the compiler, and even then UB is not usually particularly interesting). Rather, you need to concentrate on avoiding undefined behaviour.
For example, since any out-of-range cast of a floating value to an integer is undefined, you need to ensure that such casts do not involve out-of-range values. Unlike some other languages [note 1], the C++ standard does not provide an easily-recognizable result which can be tested for, so you need to test before doing the cast.
Note that DBL_MAX
is a macro, whose substitution is a string representing an approximation of the largest representable floating point number. std::numeric_limits<double>::max()
, on the other hand, is the precise largest representable floating point number.
The difference should not normally be noticeable, but (as indicated by a note in the standard in §5.20 [expr.const], para 6):
Since this International Standard imposes no restrictions on the accuracy of floating-point operations, it is unspecified whether the evaluation of a floating-point expression during translation yields the same result as the evaluation of the same expression (or the same operations on the same values) during program execution.
Although std::numeric_limits<double>::max()
is declared a constexpr
, the cast to int
is not a constant expression (as per §5.20/p2.5) precisely because its behaviour is undefined.
Upvotes: 3