8
votes

In the following code:

#include <iostream>
int main()
{
  const long l = 4294967296;
  int i = l;
  return i; //just to silence the compiler
}

the compiler warns about implicit conversion (using -Wall and -std=c++14) as following:

warning: implicit conversion from 'const long' to 'int' changes value from 4294967296 to 0 [-Wconstant-conversion]

which is ok. But there is no warning if the conversion is from double to int, as in the following code:

#include <iostream>
int main()
{
  const double d = 4294967296.0;
  int i = d;
  return i; //just to silence the compiler
}

Why the compiler reacts differently in these situations?

Note 1: clang version is 3.6.2-svn240577-1~exp1

Note 2: I've tested it with many others versions of gcc, clang and icc thanks to Compiler Explorer (gcc.godbolt.org). So all tested versions of gcc (with exception of 5.x) and icc threw the warning. No clang version did it.

2
gcc 6.1.1 issues a warning on the double-to-int conversion.Sam Varshavchik
@ArnavBorborah: Assuming 32-bit int, the value 4294967296.0 (exactly 2**32) is well outside the range of int. The conversion has undefined behavior. (I get 4196368 and no warning with clang++, 2147483647 and a warning with g++.) I'd say it's a bug in clang++.Keith Thompson
I don't know why this is being downvoted.Keith Thompson
You can get a warning if you compile with -Wconversion, but the warning occurs even for non-problematic values, for example const double d = 1.0; int i = d;Keith Thompson
@ArnavBorborah: The code in the question uses the same value for the long and double variables. If the conversion from long to int loses information, then so does the conversion from double to int. It is, after all, the same value.IInspectable

2 Answers

4
votes

The conversion from double to an integer type changes the value "by design" (think to 3.141592654 converted to an int).

The conversion from long int to int instead may or work or may be undefined behavior depending on the platform and on the value (the only guarantee is that an int is not bigger than a long int, but they may be the same size).

In other words the problems in the conversions between integer types are incidental artifacts of the implementation, not by-design decisions. Warning about them is better especially if it can be detected at compile time that something doesn't work because of those limitations.

Note also that even conversion from double to int is legal and well defined (if done within boundaries) and an implementation is not required to warn about it even when the loss of precision can be seen at compile time. Compilers that warn too much even when the use could be meaningful can be a problem (you just disable warnings or even worse get the habit of accepting a non-clean build as normal).

These implicit conversion rules may add up with other C++ wrinkles getting to truly odd-looking and hard to justify behaviors like:

std::string s;
s = 3.141592654; // No warnings, no errors (last time I checked)

Don't try to use too much logic with C++. Reading specs works better.

1
votes

Well, by reading this great article named "What Every C Programmer Should Know About Undefined Behavior", specially part #3/3, at LLVM Project Blog, written by Chris Lattner - the main author of LLVM - I could understand better the Clang's Approach to Handling Undefined Behavior.

So, in order to guarantee your strong appeal for optimization and time economy - "ultimate performance" -

Keep in mind though that the compiler is limited by not having dynamic information and by being limited to what it can without burning lots of compile time.

Clang doesn't run all related undefined behavior checks by default,

Clang generates warnings for many classes of undefined behavior (including dereference of null, oversized shifts, etc) that are obvious in the code to catch some common mistakes.

instead of this, Clang and LLVM provides tools like Clang Static Analyzer, Klee project, and the -fcatch-undefined-behavior (now UndefinedBehaviorSanitizer - UBSan - ) to avoid these possible bugs.

By running UBSan in the presented code, clang++ with the following argument -fsanitize=undefined the bug will be catched as following:

runtime error: value 4.29497e+09 is outside the range of representable values of type 'int'