Alexei Alexei - 3 months ago 18
C++ Question

C++ implicit numeric type demoting

Recently, I have noticed that C/C++ seems to be very permissible with numeric type conversion, as it implicitly casts a double to int.

Test:

Environment:

cpp.sh
,
Standard C++ 14
,
Compilation warnings all set


Code:

int intForcingFunc(double d) {
return d; // this is allowed
}

int main() {
double d = 3.1415;
double result = intForcingFunc(d);
printf("intForcingFunc result = %f\n", result);

int localRes = d; // this is allowed
printf("Local result = %d\n", localRes);

int staticCastRes = static_cast<int>(d); // also allowed
printf("Static cast result = %d\n", staticCastRes);
}


No warnings are issues during compilation.

Documentation mentions tangentially the subject, but misses the exact case of the question:


C++ is a strong-typed language. Many conversions, specially those that imply a different interpretation of the value, require an explicit conversion, known in C++ as type-casting.


I have also tried in a managed language (C#) and all these cases are not allowed (as expected):

static int intForcingFunc(double d)
{
// Not legal: Cannot implicitly convert type 'double' to 'int'
// return d;
return Convert.ToInt32(d);
}

static void Main(string[] args)
{
double d = 3.1415;
double result = intForcingFunc(d);
Console.WriteLine("intForcingFunc result = " + result);

// Not legal: Cannot implicitly convert type 'double' to 'int'
// int localRes = d;
int localRes = (int)d;
Console.WriteLine("local result = " + result);

Console.ReadLine();
}


Why is this behavior allowed in a strong-typed language? In most cases, this is undesired behavior. One reason behind this seems to be lack of arithmetic overflow detection.

Answer

Unfortunately, this behavior is inherited from C, which notoriously "trusts the programmer" in these things.

The exact warning flag for implicit floating-point to integer conversions is -Wfloat-conversion, which is also enabled by -Wconversion. For some unknown reason, -Wall, -Wextra, and -pedantic (which cpp.sh provides) don't include these flags.

If you use Clang, you can give it -Weverything to enable literally all warnings. If you use GCC, you must explicitly enable -Wfloat-conversion or -Wconversion to get a warning when doing such conversions (among other useful flags you will want to enable).

If you want, you can turn it to an error with e.g. -Werror-conversion.


C++11 even introduced a whole new safer initialization syntax, known as uniform initialization, which you can use to get warnings for the implicit conversions in your example without enabling any compiler warnings:

int intForcingFunc(double d) {
    return {d};  // warning: narrowing conversion of 'd' from 'double' to 'int' inside { }
}

int main() {
    double d{3.1415};  // allowed
    int localRes{d};  // warning: narrowing conversion of 'd' from 'double' to 'int' inside { }
}