8

I have a class for fixed-point arithmetic, of which this is the salient portion:

template <typename I, I S>
struct fixed
{
    I value;

    fixed(I i) : value(i * S) {}

    template <typename J, J T> fixed(const fixed<J, T> &fx)
    {
        if (S % T == 0)
            value = fx.value * (S / T);
        else if (T % S == 0)
            value = fx.value / (T / S);
        else
            value = S * fx.value / T;
    }

    static_assert(S >= 1, "Fixed-point scales must be at least 1.");
};

On GCC 4.4.5, the following line of code:

fixed<int, 8> f = fixed<int, 2>(1);

Generates an error:

fixed.hpp: In constructor ‘fixed<I, S>::fixed(const fixed<J, T>&) [with J = int, J T =     2, I = int, I S = 8]’:
fixed.hpp:81: error: division by zero

While there is a division by constant zero in the code - one of T/S or S/T must be zero for unequal scales - if S%T == 0 (and S is not 0), then S/T is not zero. GCC seems to be doing just enough optimization to figure out that one of my branches is guaranteed to divide by zero, but not enough optimization to figure out that branch guaranteed to not run.

I can throw #pragma GCC diagnostic ignored "-Wdiv-by-zero" in the file, but that risks masking real warnings.

What's the appropriate way to handle this situation? (Or is my analysis totally wrong and I do have a real runtime division by zero?)

  • isn't `fx.value / (T / S);` the same as `S * fx.value / T` if `T % S == 0`? – Jonas Bötel Mar 07 '11 at 09:25
  • http://www.boost.org/doc/libs/1_45_0/libs/mpl/doc/refmanual/eval-if-c.html might help – Anycorn Mar 07 '11 at 09:26
  • 1
    Interesting. I tried on VC. const int b = 0; int a = 3/b; /*error*/. const int b = 0; if (b!=0) int a = 3/b; /*still error, sometimes it's just problem of dumb compiler..*/ – user534498 Mar 07 '11 at 09:30
  • 1
    @LumpN: Only if you have infinite bits of precision to store intermediate results. –  Mar 07 '11 at 09:32
  • @user534498: I haven't tried VC either, but these are template parameters and not variables. Warning in the variable case strikes me as reasonable because if b is known to always be zero then the whole branch is unnecessary. So maybe VC is spitting out the wrong error, but I think it's still good to have a warning/error for that. –  Mar 07 '11 at 09:35

2 Answers2

7

something like?

template<int,int>
struct helper {
    static int apply(...) { return S * fx.value / T; }
};

template<int n>
struct helper<0,n> { // need 0,0 as well to avoid ambiguity
    static int apply(...) { return fx.value * (S / T); }
};

template<int m>
struct helper<m,0> {
    static int apply(...) { return fx.value / (T / S); }
};

helper<(S % T == 0), (T % S == 0)>::apply(...);

or using mpl::bool_ you could "specialize" functions through parameters.

MSalters
  • 173,980
  • 10
  • 155
  • 350
Anycorn
  • 50,217
  • 42
  • 167
  • 261
0

You could use a supporting template to do the division, and specialise it to hardcode an arbitrary value (given it won't be used) when the divisor is 0.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252