TerryZ
TerryZ

Reputation: 1

results from convert DBL_MAX to int is different from std::numeric_limits<double>::max() to int

While doing a conversion test I encounter some strange behavior in C++.

Context

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.

'Same' input, different results?

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>.

'Same' input, same results in similar situation

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++?

Sample Code for 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

Answers (1)

rici
rici

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.


Notes

  1. In Java, for example, the conversion is well defined. See the Java Language Specification for details.

Upvotes: 3

Related Questions