Search code examples
c++doubleunsigned-integer

Is it safe to rely on implicit promotion from any numeric type (unsigned, int, ...) to double?


I have a template class for graphs that takes a parameter for the weight type (could be unsigned, int or double). Also, for comparing doubles I use inline functions of the following type:

inline bool EpsLess(double x, double y, double epsilon = 1.e-10)
{
    return x < y - epsilon;
}
inline bool EpsEqual(double x, double y, double epsilon = 1.e-10)
{
    return !EpsLess(x,y) && !EpsLess(y,x);
}

Is the comparator in the following class skeleton safe ?

template <typename Weight>
class Graph
{
    struct Edge
    {
        std::string from;
        std::string to;
        Weight      weight;
    };

    struct LargestWeight
    {
        bool operator() (const Edge& e1, const Edge& e2) const
        {
            if (EpsEqual(e1.weight == e2.weight))
                if (e1.from == e2.from)
                    return e1.to < e2.to;
                else
                    return e1.from < e2.from;
            return EpsLess(e2.weight, e1.weight);
        }
    };

    // .. other stuff
};

Can I encounter unforeseen consequences when Weight type is unsigned or int ? Or is there a better way to implement the double comparison ?


Solution

  • This is exactly what templates are for.

    I would suggest that you implement EpsLess() inside a template class that uses just the < and == operators. Something like:

    template<typename Type> Compare {
    
    public:
    
        template<typename Ignore>
        inline bool EpsLess(Type x, Type y, Ignore epsilon = Ignore())
        {
            return x < y;
        }
    };
    

    Then specialize it for a double:

    template<> Compare<double> {
    
    public:
        inline bool EpsLess(double x, double y, double epsilon = 1.e-10)
        {
            return x < y - epsilon;
        }
    };
    

    You would invoke it like this:

    if (Compare<Weight>::EpsEqual(e1.weight, e2.weight))
    

    This will avoid a bunch of useless work for non-double cases, and get devolved to just an ordinary < operator.

    Your homework assignment, then, is to reimplement EpsEqual() as a template function itself, in terms of the new EpsLess().