1

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.

Community
  • 1
  • 1
Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
  • 2
    Please choose one of C and C++ for your question. There is no such thing as C/C++. – fuz Feb 06 '16 at 19:47
  • @FUZxxl - It was meant to be C or C++, but fixed it a more precise C++. – Alexei - check Codidact Feb 06 '16 at 19:50
  • 1
    Always enable compiler warnings! Questions asking why the compiler does not warn if not enabled are actually useless. – too honest for this site Feb 06 '16 at 19:51
  • @Olaf "Compilation warnings all set" – Emil Laine Feb 06 '16 at 19:51
  • Then get a better compiler. Read the man page of your compiler, you might be missing one (IOW: define "warnings all set"). And C is not strongly-typed, C++ is stronger typed, but still has some loopholes. If you want strongly typing use e.g. Python. – too honest for this site Feb 06 '16 at 19:52
  • 2
    "One reason behind this seems to be lack of arithmetic overflow detection." IMO it is better not to think of C or C++ as "lacking overflow detection", but rather that in these languages, the compiler is allowed to *assume* that overflow will never occur, in order to generate code that goes much faster. You might want to read this great article about the utility of undefined behavior in these languages and typical optimization opportunities: http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html – Chris Beck Feb 06 '16 at 20:16
  • @ChrisBeck - very good article. Being mainly a C# developer makes you forget/ignore underlying mechanisms. Thanks. – Alexei - check Codidact Feb 06 '16 at 20:58

2 Answers2

6

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 { }
}
Emil Laine
  • 41,598
  • 9
  • 101
  • 157
1

You did not specify what compiler you are working with, but you probably have not, in fact, enabled all warnings. There's a story behind it, but the net effect is that g++ -Wall does not actually enable all warnings (not even close). Others (eg. clang++), in order to be drop-in replacement-compatible with g++ must do the same.

Here is a great post on setting strong warnings for g++: https://stackoverflow.com/a/9862800/1541330

If you are using clang++, things will be much easier for you: try using -Weverything. It does just what you expect (turns on every warning). You can add -Werror and the compiler will then treat any warnings that occur as compile-time errors. If you are now seeing warnings (errors) that you want to suppress, just add -Wno-<warning-name> to your command (eg. -Wno-c++98-compat).

Now the compiler will warn you whenever an implicit narrowing conversion (conversion with possible data loss that you didn't explicitly ask for) occurs. In cases where you want a narrowing conversion to occur, you must use an appropriate cast, eg:

int intForcingFunc(double d) {
    return static_cast<int>(d);   //cast is now required
}
Community
  • 1
  • 1
U007D
  • 5,772
  • 2
  • 38
  • 44