12

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?

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622

3 Answers3

15

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.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • In my actual class I have *two* ratios, so it gets a little more complicated. But it might be possible to make a ratio template class and use something like `Ratio<0, 0>::value`. I'll have to play with it a little. – Mark Ransom Nov 14 '14 at 05:53
  • I hope you don't mind if this experiment waits. It's midnight here and I just turned into a pumpkin. – Mark Ransom Nov 14 '14 at 05:57
  • @MarkRansom: of course not... take it easy! BTW - if you think it's easier, you might try: const double B = (B2 == 0 ? 0.0 : (double) B1) / (B2 == 0 ? 1.0 : (double) B2); Cheers. – Tony Delroy Nov 14 '14 at 06:10
  • 1
    I wasn't able to use the idea of a ratio class because Visual Studio 2013 doesn't have `constexpr` yet. But your idea of using a different ternary expression worked perfectly - I still don't know why. Thanks! – Mark Ransom Nov 16 '14 at 03:59
  • @MarkRansom it's weird. The thought that led to the different ternary expression was that it's prematurely evaluating all arguments - not doing short circuit evaluation - so postponing the actual division until after a selection of divisor has been made seemed promising. Cheers. – Tony Delroy Nov 16 '14 at 19:26
2

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;
Rupesh Yadav.
  • 894
  • 2
  • 10
  • 24
  • But does the compiler recognize `B` as a compile-time constant? You'd need to view the generated assembly code to know for sure. – Mark Ransom Nov 14 '14 at 05:54
  • Version of this answer [here](http://ideone.com/TGwKt5) - with `static constexpr` - it works for gcc and is compile-time constant. +1. Cheers. – Tony Delroy Nov 14 '14 at 06:50
  • Interesting idea - however the [Compiler Error C2124](http://msdn.microsoft.com/en-us/library/byc7w2kb%28v=vs.90%29.aspx) indicates that the type conversion will be executed when evaluating a _const expression_. So, I suspect that _const expression evaluation_ is flawed. – CouchDeveloper Nov 14 '14 at 07:33
  • @TonyD IMHO, _constexpr_ is not required within the context given. IMO, the OPs expression _should_ be a const expression anyway. But the C++11 in VS2013 lacks many C++11 features - while clang and gcc are complete. – CouchDeveloper Nov 14 '14 at 07:39
  • yes agree with @CouchDeveloper, because it doesn't look OPs is asking to make it static any where in this context. – Rupesh Yadav. Nov 14 '14 at 07:46
  • @CouchDeveloper: well, [here's](http://ideone.com/aRSH0g) the code with just `const` - compiler error under GCC w/ C++11 flags as you can see - any explanation? Cheers. – Tony Delroy Nov 14 '14 at 08:20
  • but OPs didn't mentioned any where that he is going to use "B" as static? may be he just wanted to use it like this http://ideone.com/K7VcrB – Rupesh Yadav. Nov 14 '14 at 08:31
  • 1
    @TonyD In your snippet Member `B` is not a _static_ member ;) – CouchDeveloper Nov 14 '14 at 08:36
2

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 ints 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.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • Looks like voodoo. Any ideas on why this expression is optimized-out but the original one isn't? – anatolyg Nov 16 '14 at 11:58
  • @anatolyg I think the better question is why the original expression failed; both should logically avoid the divide-by-zero calculation, and others report that gcc and clang have no problem with it. I suspect Visual Studio is evaluating the right side of the ternary before it determines that it needs it. In the new formula the right side doesn't contain an error. – Mark Ransom Nov 17 '14 at 03:27