C++ 数値操作と計算の練習
はじめに
画像を拡大縮小するとき、グラフが表示範囲内に収まるようにするとき、カメラと注視点との距離を変えるとき、スライダの注目範囲を変えるときなどなどに、2倍 4倍 8倍であったり、10倍 100倍 1000倍であったりといった一定の規則で変化する数列が必要なことがあります
選択肢が固定の場合や、あまりに複雑な場合などは配列を使うのが簡単ですが、ここではいろいろな周期的な数列を計算で求めてみましょう
1, 2, 3, 4...
指定されたインデックスに +1 するだけです
これはそのままですね
実装例
inline int zoom1_i( int zoom ){ return zoom + 1; } int main(){ debug_printf( "1\n" ); for( int n = 0; n < 20; ++n ){ debug_printf( "[%2d] %d\n", n, zoom1_i( n )); } return 0; }
出力例
1 [ 0] 1 [ 1] 2 [ 2] 3 [ 3] 4 [ 4] 5 [ 5] 6 [ 6] 7 [ 7] 8 [ 8] 9 [ 9] 10 [10] 11 [11] 12 [12] 13 [13] 14 [14] 15 [15] 16 [16] 17 [17] 18 [18] 19 [19] 20
逆計算
逆の場合もそのままですね
+1 をしたので逆にする場合は -1 します
inline int indexof_zoom1( int zoom ){ return zoom - 1; } int main(){ debug_printf( "1_2\n" ); bool result = true; for( int n = 0; n < 20; ++n ){ int zoom = zoom1_i( n ); int m = indexof_zoom1( zoom ); if( m != n ){ debug_printf( "NG: %d -> %d != %d\n", zoom, m, n ); result = false; break; } } if( result ){ debug_printf( "OK\n" ); } return 0; }
1, 2, 4, 8...
2倍 4倍 8倍というような倍率は画像の拡大などで使います
プログラミングでは、2倍づつという場合は 1ビットづつシフトするだけで済みます
実装例
inline int zoom2_i( int zoom ){ return 1 << zoom; } int main(){ debug_printf( "2\n" ); for( int n = 0; n < 20; ++n ){ debug_printf( "[%2d] %d\n", n, zoom2_i( n )); } return 0; }
出力例
2 [ 0] 1 [ 1] 2 [ 2] 4 [ 3] 8 [ 4] 16 [ 5] 32 [ 6] 64 [ 7] 128 [ 8] 256 [ 9] 512 [10] 1024 [11] 2048 [12] 4096 [13] 8192 [14] 16384 [15] 32768 [16] 65536 [17] 131072 [18] 262144 [19] 524288
逆計算
2 の累乗の反対なので log2 を得るだけですが、整数用の log 関数はないので自分で書きましょう
inline int log2_i( int x ){ int result = 0; while( x >= 2 ){ x >>= 1; ++result; } return result; } inline int indexof_zoom2( int zoom ){ return log2_i( zoom ); } int main(){ debug_printf( "2_2\n" ); bool result = true; for( int n = 0; n < 20; ++n ){ int zoom = zoom2_i( n ); int m = indexof_zoom2( zoom ); if( m != n ){ debug_printf( "NG: %d -> %d != %d\n", zoom, m, n ); result = false; break; } } if( result ){ debug_printf( "OK\n" ); } return 0; }
1, 10, 100, 1000...
10倍づつということはそのまま 10 の累乗です
累乗を求めるのは、浮動小数点数なら pow 関数がありますが整数にはありません
自分で書きましょう
実装例
inline int pow10_i( int e ){ int result = 1; while( e > 0 ){ result *= 10; --e; } return result; } inline int zoom10_i( int zoom ){ return pow10_i( zoom ); } int main(){ debug_printf( "10\n" ); for( int n = 0; n < 10; ++n ){ debug_printf( "[%2d] %d\n", n, zoom10_i( n )); } return 0; }
出力例
10 [ 0] 1 [ 1] 10 [ 2] 100 [ 3] 1000 [ 4] 10000 [ 5] 100000 [ 6] 1000000 [ 7] 10000000 [ 8] 100000000 [ 9] 1000000000
逆計算
こちらも 10 の累乗の反対なので log10 を得るだけです
inline int log10_i( int x ){ int result = 0; while( x >= 10 ){ x /= 10; ++result; } return result; } inline int indexof_zoom10( int zoom ){ return log10_i( zoom ); } int main(){ debug_printf( "10_2\n" ); bool result = true; for( int n = 0; n < 10; ++n ){ int zoom = zoom10_i( n ); int m = indexof_zoom10( zoom ); if( m != n ){ debug_printf( "NG: %d -> %d != %d\n", zoom, m, n ); result = false; break; } } if( result ){ debug_printf( "OK\n" ); } return 0; }
1, 2, 10, 20, 100, 200...
これは2つおきの 1, 2, 1, 2 という変化と、2つおきに 10倍という変化の組み合わせですから、まず指定のインデックスを2つづつに区切ります
2つづつということは、最下位1ビットとそれ以外の上位ビットとに分けます
そして最下位1ビットの値を 1, 2, 1, 2 に、上位ビットの値を 10 の累乗に対応させます
実装例
inline int zoom_i( int m, int e ){ return m * pow10_i( e ); } inline int zoom12_i( int zoom ){ int e = zoom >> 1; int n = zoom & 1; int m = n + 1; return zoom_i( m, e ); } int main(){ debug_printf( "12\n" ); for( int n = 0; n < 20; ++n ){ debug_printf( "[%2d] %d\n", n, zoom12_i( n )); } return 0; }
出力例
12 [ 0] 1 [ 1] 2 [ 2] 10 [ 3] 20 [ 4] 100 [ 5] 200 [ 6] 1000 [ 7] 2000 [ 8] 10000 [ 9] 20000 [10] 100000 [11] 200000 [12] 1000000 [13] 2000000 [14] 10000000 [15] 20000000 [16] 100000000 [17] 200000000 [18] 1000000000 [19] 2000000000
逆計算
0 を除いた余りを求めれば 1, 2 から 0, 1 を求めるだけなので -1 するだけですね
inline int indexof_zoom12( int zoom ){ int e = log10_i( zoom ); int m = zoom / pow10_i( e ); int n = m - 1; return ( e << 1 ) + n; } int main(){ debug_printf( "12_2\n" ); bool result = true; for( int n = 0; n < 20; ++n ){ int zoom = zoom12_i( n ); int m = indexof_zoom12( zoom ); if( m != n ){ debug_printf( "NG: %d -> %d != %d\n", zoom, m, n ); result = false; break; } } if( result ){ debug_printf( "OK\n" ); } return 0; }
1, 5, 10, 50, 100, 500...
1, 2, 10, 20, 100, 200... の場合と同じですが、最下位1ビットの値を 1, 2 ではなく 1, 5 に対応させます
実装例
inline int zoom15_i( int zoom ){ int e = zoom >> 1; int n = zoom & 1; int m = ( n << 2 ) + 1; return zoom_i( m, e ); } int main(){ debug_printf( "15\n" ); for( int n = 0; n < 10; ++n ){ debug_printf( "[%2d] %d\n", n, zoom15_i( n )); } return 0; }
出力例
15 [ 0] 1 [ 1] 5 [ 2] 10 [ 3] 50 [ 4] 100 [ 5] 500 [ 6] 1000 [ 7] 5000 [ 8] 10000 [ 9] 50000
逆計算
1, 5 から 0, 1 を求めます
2ビットシフトするとちょうどよさそうです
inline int indexof_zoom15( int zoom ){ int e = log10_i( zoom ); int m = zoom / pow10_i( e ); int n = m >> 2; return ( e << 1 ) + n; } int main(){ debug_printf( "15_2\n" ); bool result = true; for( int n = 0; n < 10; ++n ){ int zoom = zoom15_i( n ); int m = indexof_zoom15( zoom ); if( m != n ){ debug_printf( "NG: %d -> %d != %d\n", zoom, m, n ); result = false; break; } } if( result ){ debug_printf( "OK\n" ); } return 0; }
1, 2, 3, 10, 20, 30, 100, 200, 300...
これも基本的には 1, 2, 10, 20, 100, 200... の場合と同じですが、こちらは3つづつ分けます
3つづつということは、3で割った商と剰余とに分けます
実装例
inline int zoom123_i( int zoom ){ int e = zoom / 3; int n = zoom % 3; int m = n + 1; return zoom_i( m, e ); } int main(){ debug_printf( "123\n" ); for( int n = 0; n < 20; ++n ){ debug_printf( "[%2d] %d\n", n, zoom123_i( n )); } return 0; }
出力例
123 [ 0] 1 [ 1] 2 [ 2] 3 [ 3] 10 [ 4] 20 [ 5] 30 [ 6] 100 [ 7] 200 [ 8] 300 [ 9] 1000 [10] 2000 [11] 3000 [12] 10000 [13] 20000 [14] 30000 [15] 100000 [16] 200000 [17] 300000 [18] 1000000 [19] 2000000
逆計算
1, 2, 3 から 0, 1, 2 を求めます
inline int indexof_zoom123( int zoom ){ int e = log10_i( zoom ); int m = zoom / pow10_i( e ); int n = m - 1; return ( e * 3 ) + n; } int main(){ debug_printf( "123_2\n" ); bool result = true; for( int n = 0; n < 20; ++n ){ int zoom = zoom123_i( n ); int m = indexof_zoom123( zoom ); if( m != n ){ debug_printf( "NG: %d -> %d != %d\n", zoom, m, n ); result = false; break; } } if( result ){ debug_printf( "OK\n" ); } return 0; }
1, 2, 3, 4, 10, 20, 30, 40, 100, 200, 300...
これも基本的には 1, 2, 10, 20, 100, 200... の場合と同じですが、こちらは4つづつ分けます
4つづつということは、最下位2ビットとそれ以外の上位ビットとに分けます
実装例
inline int zoom1234_i( int zoom ){ int e = zoom >> 2; int n = zoom & 3; int m = n + 1; return zoom_i( m, e ); } int main(){ debug_printf( "1234\n" ); for( int n = 0; n < 20; ++n ){ debug_printf( "[%2d] %d\n", n, zoom1234_i( n )); } return 0; }
出力例
1234 [ 0] 1 [ 1] 2 [ 2] 3 [ 3] 4 [ 4] 10 [ 5] 20 [ 6] 30 [ 7] 40 [ 8] 100 [ 9] 200 [10] 300 [11] 400 [12] 1000 [13] 2000 [14] 3000 [15] 4000 [16] 10000 [17] 20000 [18] 30000 [19] 40000
逆計算
1, 2, 3, 4 から 0, 1, 2, 3 を求めます
inline int indexof_zoom1234( int zoom ){ int e = log10_i( zoom ); int m = zoom / pow10_i( e ); int n = m - 1; return ( e << 2 ) + n; } int main(){ debug_printf( "1234_2\n" ); bool result = true; for( int n = 0; n < 20; ++n ){ int zoom = zoom1234_i( n ); int m = indexof_zoom1234( zoom ); if( m != n ){ debug_printf( "NG: %d -> %d != %d\n", zoom, m, n ); result = false; break; } } if( result ){ debug_printf( "OK\n" ); } return 0; }
1, 2, 4, 8, 10, 20, 40, 80, 100, 200, 400...
4つづつなので、こちらも最下位2ビットとそれ以外の上位ビットとに分けます
最下位2ビットの値が 1, 2, 4, 8 の変化に対応し、上位ビットの値は 10 の累乗に対応します
実装例
inline int zoom1248_i( int zoom ){ int e = zoom >> 2; int n = zoom & 3; int m = 1 << n; return zoom_i( m, e ); } int main(){ debug_printf( "1248\n" ); for( int n = 0; n < 20; ++n ){ debug_printf( "[%2d] %d\n", n, zoom1248_i( n )); } return 0; }
出力例
1248 [ 0] 1 [ 1] 2 [ 2] 4 [ 3] 8 [ 4] 10 [ 5] 20 [ 6] 40 [ 7] 80 [ 8] 100 [ 9] 200 [10] 400 [11] 800 [12] 1000 [13] 2000 [14] 4000 [15] 8000 [16] 10000 [17] 20000 [18] 40000 [19] 80000
逆計算
1, 2, 4, 8 から 0, 1, 2, 3 を求めます
log2 でちょうどよさそうですね
inline int indexof_zoom1248( int zoom ){ int e = log10_i( zoom ); int m = zoom / pow10_i( e ); int n = log2_i( m ); return ( e << 2 ) + n; } int main(){ debug_printf( "1248_2\n" ); bool result = true; for( int n = 0; n < 20; ++n ){ int zoom = zoom1248_i( n ); int m = indexof_zoom1248( zoom ); if( m != n ){ debug_printf( "NG: %d -> %d != %d\n", zoom, m, n ); result = false; break; } } if( result ){ debug_printf( "OK\n" ); } return 0; }
1, 3, 5, 7, 10, 30, 50, 70, 100, 300, 500...
1, 2, 4, 8, 10, 20, 40, 80, 100, 200, 400... の場合と同じですが、こちらは下位2ビットの値を 1, 3, 5, 7 に対応させます
0, 1, 2, 3 を2倍して 0, 2, 4, 6、さらに +1 すれば 1, 3, 5, 7 になりました
実装例
inline int zoom1357_i( int zoom ){ int e = zoom >> 2; int n = zoom & 3; int m = ( n << 1 ) + 1; return zoom_i( m, e ); } int main(){ debug_printf( "1357\n" ); for( int n = 0; n < 20; ++n ){ debug_printf( "[%2d] %d\n", n, zoom1357_i( n )); } return 0; }
出力例
1357 [ 0] 1 [ 1] 3 [ 2] 5 [ 3] 7 [ 4] 10 [ 5] 30 [ 6] 50 [ 7] 70 [ 8] 100 [ 9] 300 [10] 500 [11] 700 [12] 1000 [13] 3000 [14] 5000 [15] 7000 [16] 10000 [17] 30000 [18] 50000 [19] 70000
逆計算
1, 3, 5, 7 から 0, 1, 2, 3 を求めます
1ビットシフトするだけでよさそうですね
inline int indexof_zoom1357( int zoom ){ int e = log10_i( zoom ); int m = zoom / pow10_i( e ); int n = m >> 1; return ( e << 2 ) + n; } int main(){ debug_printf( "1357_2\n" ); bool result = true; for( int n = 0; n < 20; ++n ){ int zoom = zoom1357_i( n ); int m = indexof_zoom1357( zoom ); if( m != n ){ debug_printf( "NG: %d -> %d != %d\n", zoom, m, n ); result = false; break; } } if( result ){ debug_printf( "OK\n" ); } return 0; }
1, 2, 5, 10, 20, 50, 100, 200, 500...
UI の調整で 10倍 100倍 1000倍では変化が粗すぎるというような場合に使います
1, 2, 3, 10, 20, 30, 100, 200, 300... の場合と同じですが、こちらは 1, 2, 3 ではなく 1, 2, 5 とします
0, 1, 2 を3倍して 0, 3, 6、さらに -1 して -1, 2, 5、絶対値をとると 1, 2, 5 になりました
実装例
inline int abs_i( int n ){ return ( n < 0 ) ? (-n) : n; } inline int zoom125_i( int zoom ){ int e = zoom / 3; int n = zoom % 3; int m = abs_i( n * 3 - 1 ); return zoom_i( m, e ); } int main(){ debug_printf( "125\n" ); for( int n = 0; n < 20; ++n ){ debug_printf( "[%2d] %d\n", n, zoom125_i( n )); } return 0; }
出力例
125 [ 0] 1 [ 1] 2 [ 2] 5 [ 3] 10 [ 4] 20 [ 5] 50 [ 6] 100 [ 7] 200 [ 8] 500 [ 9] 1000 [10] 2000 [11] 5000 [12] 10000 [13] 20000 [14] 50000 [15] 100000 [16] 200000 [17] 500000 [18] 1000000 [19] 2000000
実はこの計算はこんなことをしなくてももっと簡単にできます
どうすればいいでしょうか
0, 1, 2 の 2 だけ2ビット目が立っていますから、これを利用すると簡単になります
inline int zoom125_i( int zoom ){ int e = zoom / 3; int n = zoom % 3; int m = n + ( n & 2 ) + 1; return zoom_i( m, e ); }
逆計算
1, 2, 5 から 0, 1, 2 を求めます
これも 1ビットシフトするだけでよさそうですね
inline int indexof_zoom125( int zoom ){ int e = log10_i( zoom ); int m = zoom / pow10_i( e ); int n = m >> 1; return ( e * 3 ) + n; } int main(){ debug_printf( "125_2\n" ); bool result = true; for( int n = 0; n < 20; ++n ){ int zoom = zoom125_i( n ); int m = indexof_zoom125( zoom ); if( m != n ){ debug_printf( "NG: %d -> %d != %d\n", zoom, m, n ); result = false; break; } } if( result ){ debug_printf( "OK\n" ); } return 0; }
1, 2, 3, 5, 10, 20, 30, 50, 100, 200, 300...
4つづつなので、こちらも最下位2ビットとそれ以外の上位ビットとに分けます
最下位2ビットの値が 1, 2, 3, 5 の変化に対応し、上位ビットの値は 10 の累乗に対応します
0, 1, 2, 3 に +1 すると 1, 2, 3, 4 で、4 だけ3ビット目が立っているので、これを利用します
実装例
inline int zoom1235_i( int zoom ){ int e = zoom >> 2; int n = zoom & 3; int m = n + (( n + 1 ) >> 2 ) + 1; return zoom_i( m, e ); } int main(){ debug_printf( "1235\n" ); for( int n = 0; n < 20; ++n ){ debug_printf( "[%2d] %d\n", n, zoom1235_i( n )); } return 0; }
出力例
1235 [ 0] 1 [ 1] 2 [ 2] 3 [ 3] 5 [ 4] 10 [ 5] 20 [ 6] 30 [ 7] 50 [ 8] 100 [ 9] 200 [10] 300 [11] 500 [12] 1000 [13] 2000 [14] 3000 [15] 5000 [16] 10000 [17] 20000 [18] 30000 [19] 50000
逆計算
1, 2, 3, 5 から 0, 1, 2, 3 を求めます
こちらも3ビット目を使えそうです
inline int indexof_zoom1235( int zoom ){ int e = log10_i( zoom ); int m = zoom / pow10_i( e ); int n = ( m - 1 ) - ( m >> 2 ); return ( e << 2 ) + n; } int main(){ debug_printf( "1235_2\n" ); bool result = true; for( int n = 0; n < 20; ++n ){ int zoom = zoom1235_i( n ); int m = indexof_zoom1235( zoom ); if( m != n ){ debug_printf( "NG: %d -> %d != %d\n", zoom, m, n ); result = false; break; } } if( result ){ debug_printf( "OK\n" ); } return 0; }