简体   繁体   中英

How to eliminate “divide by 0” error in template code

I'm using a pair of integer template parameters to specify a ratio, since I can't use a double as a template parameter. The conversion into a double is protected against divide-by-zero with a ternary. This worked in an earlier version of the compiler, but Visual Studio 2013 gives an error:

error C2124: divide or mod by zero

Here's a simplified version of the code:

template<int B1, int B2>
class MyClass
{
    const double B = (B2 == 0) ? 0.0 : (double) B1 / (double) B2;
    // ...
};

MyClass<0, 0> myobj;

I really want B to be optimized out of expressions that use it when it's zero, so I need the single-line definition. I know I can just use template parameters <0, 1> to get around it, but I wonder if there's a way to just convince the compiler that my expression is safe?

What I'm told worked:

 const double B = (B2 == 0 ? 0.0 : (double) B1) /
                  (B2 == 0 ? 1.0 : (double) B2);

This avoids a reliance on short circuit evaluation preventing the divide by 0; having the conditional selections happen before the division.


Original idea / Perhaps something like this...? (I think B should be static const or constexpr , but I'm sure you can sort that...)

template<int B1, int B2>
struct MyClass
{
    const double B = (double) B1 / (double) B2;
};

template <int B1>
struct MyClass<B1, 0>
{
    const double B = 0.0;
};

If there's lots of other stuff you want in MyClass and don't want to duplicate or put in a base etc., you could move the B calculation into a supporting template using the specialisation approach above.

Visual Studio is not able to typecast B1, B2 in ternary operation at compile time, but explicitly casting will work.

template<int B1, int B2>
class MyClass
{
    double d1 = (double)B1;
    double d2 = (double)B2;
    const double B = (B2 == 0) ? 0.0 : d1/d2;
    // ...
};

MyClass<0, 0> myobj;

For the curious - here's the code I finally ended up with. It probably helps to see it in a real world context.

template<int B1, int B2, int C1, int C2>
class BicubicFilter
{
    // Based on the formula published by Don Mitchell and Arun Netravali at
    // http://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf
public:
    BicubicFilter() : m_dMultiplier(1.0) {}
    double m_dMultiplier;
    double k(double x) const
    {
        const double B = (double) B1 / ((B2 == 0) ? 1.0 : (double) B2);
        const double C = (double) C1 / ((C2 == 0) ? 1.0 : (double) C2);
        x = fabs(x) * m_dMultiplier;
        if (x < 1.0)
            return ((2.0 - 1.5*B - C) * x*x*x) + ((-3.0 + 2.0*B + C) * x*x) + (1.0 - (2.0/6.0)*B);
        if (x < 2.0)
            return (((-1.0/6.0)*B - C) * x*x*x) + ((B + 5.0*C) * x*x) + ((-2.0*B - 8.0*C) * x) + ((8.0/6.0)*B + 4.0*C);
        return 0.0;
    }
};

The k function is executed at least 4 times per pixel for an image resizing operation, so efficiency is critical. I want all the constants known at compile time so the compiler can simplify the expressions as much as possible.

Based on the accepted answer, I had hoped to create a Ratio template class that would simply produce the ratio of two int s as a constexpr double and specialize it for parameters of 0, 0 . Visual Studio 2013 does not yet implement constexpr so I wasn't confident that the compiler would treat it as a compile-time constant. Fortunately a variation on the original ternary expression eliminated the error.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM