Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compiler does not warn about precision loss?

I have been playing around with C++11 lately, and came up with the following sum function:

template <typename T>
inline T sum(const std::function<T (int)> &f, int initial, int end)
{
    T retval = 0;
    for(int k = initial; k <= end; k++) {
        retval += f(k);
    }
    return retval;
}

The idea is that I can pass a lambda function and thus have a neat and readable function for mathematical sums. I then tried the following:

int main()
{
    std::array<double, 2> arr1 = {{ 0.5, 1.5 }},
                          arr2 = {{ 1.5, -0.5 }};
    auto n = sum<int>([&](int k) { return arr1[k]*arr2[k]; }, // Precision loss!
                      0, 1);
    return 0;
}

I compiled this using g++ 4.6.3: g++ -Wall -pedantic -std=c++0x -o test main.cpp and does not give any warning about the precision loss I remarked in the comment of the source code.

It's pretty much a given here that sum<int> is a bad thing to do, but it might not be that obvious in more complex contexts. Should the compiler not notice that return value of my lambda function is double and warn me that I am losing out on precision when casting to int? Is there a specific reason it does not?

like image 336
nijansen Avatar asked Apr 12 '12 12:04

nijansen


3 Answers

Seems entirely reasonable. You're telling the compiler not to bother with Template Argument Deduction (i.e. use sum<double>) but instead tell it explicitly to use sum<int>. That's as good as an explicit cast.

[Edit] What about

template <typename F>
auto sum(F const& f, int initial, int end) -> decltype(f(initial))
{
    auto retval = f(initial++);
    while(initial <= end) {
        retval += f(initial++);
    }
    return retval;
}
like image 121
MSalters Avatar answered Sep 24 '22 07:09

MSalters


VS11 does issue a warning:

Warning 1 warning C4189: 'n' : local variable is initialized but not referenced
Warning 2 warning C4244: '+=' : conversion from 'double' to 'int', possible loss of data

Edit, actually that warning is from using the code:

template <typename T,typename Func>
inline T sum(Func f, int initial, int end)

You get a different warning about the bad conversion if you use std::function<T (int)>, so VS is still good on this issue. (IMO, you should generally take functors as a templated type rather than std::function)

Even clang with -Weverything doesn't issue a warning about this (Edit: although I can't test the std::function version with clang ATM). Seems like something that could be improved.

I do get this strange warning though:

ConsoleApplication1.cpp:15:51: warning: will never be executed [-Wunreachable-code]
    auto n = sum<int>([&](int k) { return arr1[k]*arr2[k]; }, // Precision loss!
                                                  ^~~~
like image 32
bames53 Avatar answered Sep 23 '22 07:09

bames53


If you unconstrain the functor and if you change the line retval += f(k); to retval += T { f(k) }; in the following way:

// Machinery to allow caller to indifferently use
// sum(f, i, j) and sum<R>(f, i, j)
struct deduced {};

template<
    typename Request = deduced
    , typename Functor
    , typename Ret = typename std::conditional<
        std::is_same<Request, deduced>::value
        , typename std::result_of<Functor&(int)>::type
        , Request
    >::type
>
inline Ret sum(Functor f, int initial, int end)
{
    Ret retval = 0;
    for(int k = initial; k <= end; k++) {
        retval += Ret { f(k) };
    }
    return retval;
}

then instead of relying on the compiler's willingness to warn, you make it required to emit a diagnostic as a narrowing conversion is disallowed within list-initialization (i.e. initialization with braces).

I don't think there's a reliable way if you constrain the functor to an std::function<Sig>. That depends solely on how the implementation wrote std::function, when it issues warnings for narrowing conversions, and even whether it warns for its own code.

like image 21
Luc Danton Avatar answered Sep 21 '22 07:09

Luc Danton