こんにちは。
下駄を履かせて符号なし型へ変換して処理すれば大丈夫と思います。
一旦C++14で作ってC++11へ置き換えました。C++11のconstexprでは鬼のように見にくいです。
C++
1 # include <iostream>
2 # include <type_traits>
3 # include <limits>
4
5 template < bool con , typename T > using enable_if_t = typename std :: enable_if < con , T > :: type ;
6
7 template < typename T , enable_if_t < std :: is_unsigned < T > :: value , std :: nullptr_t > = nullptr >
8 constexpr T a ( T min , T max , T n )
9 {
10 return ( n < min )
11 ? a ( min , max , n + ( max - min + 1 ) * ( ( min - n ) / ( max - min + 1 ) + 1 ) )
12 : ( n - min ) % ( max - min + 1 ) + min ;
13 }
14
15 template < typename T , enable_if_t < std :: is_integral < T > :: value && std :: is_signed < T > :: value , std :: nullptr_t > = nullptr >
16 constexpr T a ( T min , T max , T n )
17 {
18 # if 0 // original
19 return ( ( n - min ) % ( max - min + 1 ) + max - min + 1 ) % ( max - min + 1 ) + min ;
20 # elif 0 // need c++14
21 typedef typename std :: make_unsigned < T > :: type Unsigned ;
22 Unsigned on = static_cast < Unsigned > ( std :: numeric_limits < T > :: max ( ) ) + 1 + n ;
23 Unsigned omin = static_cast < Unsigned > ( std :: numeric_limits < T > :: max ( ) ) + 1 + min ;
24 Unsigned omax = static_cast < Unsigned > ( std :: numeric_limits < T > :: max ( ) ) + 1 + max ;
25 Unsigned omod = ( omax - omin + 1 ) ;
26 return ( omod == 0 ) ? n : ( a ( omin , omax , on ) - ( std :: numeric_limits < T > :: max ( ) + 1 ) ) ;
27 # else // need c++11
28 typedef typename std :: make_unsigned < T > :: type Unsigned ;
29 return ( ( min == std :: numeric_limits < T > :: min ( ) )
30 && ( max == std :: numeric_limits < T > :: max ( ) ) )
31 ? n
32 : ( a (
33 static_cast < Unsigned > ( std :: numeric_limits < T > :: max ( ) ) + 1 + min ,
34 static_cast < Unsigned > ( std :: numeric_limits < T > :: max ( ) ) + 1 + max ,
35 static_cast < Unsigned > ( std :: numeric_limits < T > :: max ( ) ) + 1 + n
36 ) - ( std :: numeric_limits < T > :: max ( ) + 1 ) ) ;
37 # endif
38 }
39
40 int main ( )
41 {
42 std :: cout << a ( - 100 , 100 , 12 ) << "\n" ;
43 std :: cout << a ( 100 , 200 , 12 ) << "\n" ;
44 std :: cout << a ( - 200 , - 100 , - 99 ) << "\n" ;
45 std :: cout << a ( - 200 , - 100 , 12 ) << "\n" ;
46 std :: cout << a ( std :: numeric_limits < int > :: min ( ) , 1000 , 12 ) << "\n" ;
47 std :: cout << a ( std :: numeric_limits < int > :: min ( ) , std :: numeric_limits < int > :: max ( ) , 12 ) << "\n" ;
48
49 return 0 ;
50 }
【追加】
書き忘れてました。下駄を履かせればOKと思う根拠は下記です。
少し詳しい型変換の説明 をみてます。「JIS X 3010 プログラム言語C」を参照していると書かれているので、規格的に大丈夫と期待。
符号なし整数と符号付き整数の演算は定義されてます。
unsined と intの演算は、intがunsignedへ変換されます。intをunsigendへ変換する時、intが負ならunsignedの最大値+1を加えて、intが2の補数表現でなくても2の補数表現の時と同じ結果になるよう定義されているようです。
【更に追加】
returnの計算に処理系依存が入ってましたので修正しました。
ついでにマクロを使ってC++11でも多少みやすくしました。(本音:C++14までメンテするのは面倒)
C++
1 # include <iostream>
2 # include <type_traits>
3 # include <limits>
4 # include <typeinfo>
5
6 template < typename T >
7 constexpr typename std :: make_signed < T > :: type removeExcess ( T value )
8 {
9 typedef typename std :: make_signed < T > :: type Signed ;
10 # define LIMIT ( static_cast < T > ( std :: numeric_limits < Signed > :: max ( ) ) + 1 )
11 return ( LIMIT < value ) ? ( value - LIMIT ) : ( static_cast < Signed > ( value ) - std :: numeric_limits < Signed > :: max ( ) - 1 ) ;
12 }
13
14 template < bool con , typename T > using enable_if_t = typename std :: enable_if < con , T > :: type ;
15
16 template < typename T , enable_if_t < std :: is_unsigned < T > :: value , std :: nullptr_t > = nullptr >
17 constexpr T a ( T min , T max , T n )
18 {
19 return ( n < min )
20 ? a ( min , max , n + ( max - min + 1 ) * ( ( min - n ) / ( max - min + 1 ) + 1 ) )
21 : ( n - min ) % ( max - min + 1 ) + min ;
22 }
23
24 template < typename T , enable_if_t < std :: is_integral < T > :: value && std :: is_signed < T > :: value , std :: nullptr_t > = nullptr >
25 constexpr T a ( T min , T max , T n )
26 {
27 # if 0 // original
28 return ( ( n - min ) % ( max - min + 1 ) + max - min + 1 ) % ( max - min + 1 ) + min ;
29 # else
30 typedef typename std :: make_unsigned < T > :: type Unsigned ;
31 # define N ( static_cast < Unsigned > ( std :: numeric_limits < T > :: max ( ) ) + 1 + n )
32 # define MIN ( static_cast < Unsigned > ( std :: numeric_limits < T > :: max ( ) ) + 1 + min )
33 # define MAX ( static_cast < Unsigned > ( std :: numeric_limits < T > :: max ( ) ) + 1 + max )
34 # define MOD ( MAX - MIN + 1 )
35 return ( MOD == 0 ) ? n : removeExcess ( a ( MIN , MAX , N ) ) ;
36 # endif
37 }
38
39 int main ( )
40 {
41 std :: cout << a ( - 100 , 100 , 12 ) << "\n" ;
42 std :: cout << a ( 100 , 200 , 12 ) << "\n" ;
43 std :: cout << a ( - 200 , - 100 , - 99 ) << "\n" ;
44 std :: cout << a ( - 200 , - 100 , 12 ) << "\n" ;
45 std :: cout << a ( std :: numeric_limits < int > :: min ( ) , 1000 , 12 ) << "\n" ;
46 std :: cout << a ( std :: numeric_limits < int > :: min ( ) , std :: numeric_limits < int > :: max ( ) , 12 ) << "\n" ;
47
48 return 0 ;
49 }