2010-06-23 75 views
1

我在C#中有一個方法,如下所示(在一個範圍內包裝一個數字,比如0到360 ...如果你傳遞0-359,你會得到相同的值,如果你傳遞360,你得到0,361得到1,等):在C++模板中輸入條件?

/// <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; 
    } 

我還需要在C++這種方法,我定義如下:

/*! 
    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; 
} 

現在我的問題是:我會爲無窮正確的C++版本檢查?我看不出任何方式說「如果加倍,做這些檢查,如果是浮動,做這些檢查」。如果它不是我想要的類型,它會返回false嗎? 另外,爲什麼%運算符未定義爲float和double?我想我必須自己實現模運算符。該方法非常適用於數字類型 - byte,short,int,long,float,double。

+2

「如果雙,做這些檢查,如果浮動,做這些檢查」 - >這是模板專業化是什麼 – Colin 2010-06-23 18:37:34

+0

是啊,我忘了所有關於專業化......但這些都是個別方法需要專門化,所以它更重載而不是專業化。 – 2010-06-23 21:13:05

回答

3

與numeric_limits提供的設施,你並不真的需要使用任何複雜的專業化或類似的東西爲無限檢查。

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 
} 

您的最後一步,涉及operator%將會更加複雜。您將需要提供一個自定義的mod函數,該函數被重載以將浮點類型傳遞到std::modf而不是使用operator%。你可以使用類型特徵[通過boost或TR1]來儘量減少這個重複的方面,儘管我不確定最優雅的方法是什麼。也許沿着線的東西:

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 % 
} 
+0

它區分正面和負面的無限,或不是?只需檢查無限就足夠了? – 2010-06-23 20:26:33

+0

@Jake:我只是將'value'的絕對值與正無窮大進行比較。如果你需要兩個極端的不同行爲,你可以檢查'-std :: numeric_limits :: infinity()'的值。 – 2010-06-23 20:34:04

+0

我明白了。對於模我最終定義了一個模板方法,並專門/重載它爲float和double來使用'std :: fmod'(我想你的意思是說std​​ :: fmod早些時候,而不是std :: modf,順便說一下)。所以最後我有一個用於交換的模板方法,一個用於包裝的模板方法,一個用於模塊的模板方法以及兩個專用 - 一個float和一個double。謝謝各位的幫助! – 2010-06-23 21:16:20

0

你有浮力模數的fmod(在cmath中)。小心符號約定(結果與第一個操作數具有相同的符號)。

檢查無窮大是好的。但你忘了檢查NaN。

2

科林的評論的一個具體的例子:

#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; 

} 

其中產量:

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