tags:

views:

114

answers:

3

I have a method in C# as follows (which wraps a number across a range, say 0 to 360... if you pass 0-359 you get the same value, if you pass 360 you get 0, 361 gets 1, etc.):

    /// <summary>
    /// Wraps the value across the specified boundary range.
    /// 
    /// If the value is in the range <paramref name="min"/> (inclusive) to <paramref name="max"/> (exclusive),
    /// <paramref name="value"/> will be returned. If <paramref name="value"/> is equal to <paramref name="max"/>,
    /// <paramref name="min"/> will be returned. The method essentially creates a loop between <paramref name="min"/>
    /// and <paramref name="max"/>.
    /// </summary>
    /// <param name="value">The value to wrap.</param>
    /// <param name="min">The minimum value of the boundary range, inclusive.</param>
    /// <param name="max">The maximum value of the boundary range, exclusive.</param>
    /// <returns>The value wrapped across the specified range.</returns>
    public static T Wrap<T>(T value, T min, T max) where T : IComparable<T>
    {
        // If it's positive or negative infinity, we just return the minimum, which is the "origin"
        bool infinityDouble = typeof(T) == typeof(double) && (double.IsPositiveInfinity(Convert.ToDouble(value)) || double.IsNegativeInfinity(Convert.ToDouble(value)));
        bool infinityFloat = typeof(T) == typeof(float) && (float.IsPositiveInfinity(Convert.ToSingle(value)) || float.IsNegativeInfinity(Convert.ToSingle(value)));
        if (infinityDouble || infinityFloat)
        {
            return min;
        }

        // If the value is between the origin (inclusive) and the maximum value (exclusive), just return the value
        if (value.CompareTo(min) >= 0 && value.CompareTo(max) < 0)
        {
            return value;
        }

        // The range of the wrapping function
        var range = (dynamic)max - (dynamic)min;

        return ((((value % range) + range) - min) % range) + min;
    }

I also needed this method in C++, which I defined as follows:

/*!
    Wraps the value across the specified boundary range.

    If the value is in the range \a min (inclusive) to \a max (exclusive), \a value will be returned.
    If \a value is equal to \a max, \a min will be returned. The method essentially creates a loop between
    \a min and \a max.

    \param value The value to wrap.
    \param min The minimum value of the boundary range, inclusive.
    \param max The maximum value of the boundary range, exclusive.
    \return The value wrapped across the specified range.
 */
template <typename T> const T& MathHelper::wrap(const T &value, const T &min, const T &max)
{
    // If it's positive or negative infinity, we just return the minimum, which is the "origin"
    bool infinityDouble = value == std::numeric_limits<double>::infinity() || value == -std::numeric_limits<double>::infinity();
    bool infinityFloat = value == std::numeric_limits<float>::infinity() || value == -std::numeric_limits<float>::infinity();
    if (infinityDouble || infinityFloat)
    {
        return min;
    }

    // If the value is between the origin (inclusive) and the maximum value (exclusive), just return the value
    if (value >= min && value < max)
    {
        return value;
    }

    // The range of the wrapping function
    T range = max - min;

    return ((((value % range) + range) - min) % range) + min;
}

Now my question is: am I checking for infinity correctly in the C++ version? I can't see any way to say "if double, do these checks, if float, do these checks". If it's not the type I want, will it just return false? Also, why is the % operator not defined for float and double? I guess I'll have to implement the modulo operator myself. The method is pretty much intended for numeric types - byte, short, int, long, float, double.

A: 

You have fmod for float modulus (in cmath). Beware of the sign convention (result has same sign as first operand).

The check for infinity is fine. You forgot to check for NaN however.

Alexandre C.
+2  A: 

A concrete example of Colin's comment:

#include <iostream>
#include <limits>

template<typename T>
class Foo {
    public:
    const T& wrap (const T& v, const T& min, const T& max) {
        // ...
        return v;
    }

};

template<>
class Foo<double> {
    public:
    const double& wrap (const double& v, const double& miun, const double& max) {
        if (v == std::numeric_limits<double>::infinity ()) {
            std::cout << "It was infinity" << std::endl;
        }
        return v;
    }
};


int main () {
    Foo<double> fd;
    Foo<long> fl;

    std::cout << "fl.wrap(42, 0, 100)      : " << fl.wrap(42, 0, 100) << std::endl;
    std::cout << "fd.wrap(inf, 0.0, 100.0) : " <<
        fd.wrap (std::numeric_limits<double>::infinity (), 0.0, 100.0) << std::endl;
    return 0;

}

Which yields:

fl.wrap(42, 0, 100)      : 42
It was infinity
fd.wrap(inf, 0.0, 100.0) : inf
ezpz
+3  A: 

With the facilities provided by numeric_limits, you don't really need to use any complex specializations or anything like that for the infinity check.

template <typename T> 
const T& MathHelper::wrap(const T &value, const T &min, const T &max) {
  bool isInfinity = std::numeric_limits<T>::has_infinity() 
                 && (std::abs(value) == std::numeric_limits<T>::infinity());
  //the rest
}

Your final step, involving operator% will be more complicated. You will need to provide a custom mod function, that is overloaded to pass the floating point types into std::modf instead of using operator%. You might be able to use type traits [either via boost or TR1] to minimize the repetative aspects of this, although I'm not sure what the most elegant method of doing so would be. Perhaps something along the lines of:

template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type mod(T, T) {
   //use std::modf
}

template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type mod(T, T) {
   //use %
}
Dennis Zickefoose
Does it differentiate between positive and negative infinity, or no? Checking just for infinity is sufficient?
Jake Petroules
@Jake: I just took the absolute value of `value` and compared it to positive infinity. You can check value against `-std::numeric_limits<T>::infinity()` if you want different behavior for the two extremes.
Dennis Zickefoose
I see. For the modulo I ended up defining a template method, and specializing/overloading it for float and double to use `std::fmod` (I think you meant to say std::fmod earlier, not std::modf, by the way). So in the end I have one template method for swap, one template method for wrap, one template method for modulo, and two specializations of that - one float and one double. Thanks for all the help, everyone!
Jake Petroules
@Dennis: I'm being pedantic here but `is_integral` is using specialization. Even so, your `mod` approach is clean and simple.
ezpz