整数a, b (a < b)が与えられたときに区間[a, b) = {a+1, a+2, ..., b-1}にある整数の個数はb - a個. 1ずつではなくs個ずつ増える場合は(b - a + s - 1) / s個. では
int n = (b - a + s - 1) / s;
としたときの問題点を考えてみよう.
まず符号付き整数の加減算の結果がその整数の型で表現できる最大の値(INT_MAX),最小の値(INT_MIN)を超えると処理系依存になることに注意する.
# 符号無しの場合は結果がモジュロされるので処理系依存にはならない.
以前書いたが, 符号付き数値の負の値の右シフトも処理系依存. とりあえず符号付き数値の扱いは注意が必要.
sizeof(int) = 4, INT_MAX = 0x7fffffffの処理系を考えてみよう. すると上の式はたとえばb = 0x7fffffff, a = 0, s = 2のときb - a + s - 1は途中でINT_MAXを超えるので処理系依存. 途中で + sをやめて式の外に出して, 代わりに最後に + 1してみよう.
int n = (b - a - 1) / s + 1;
とするとこれなら大丈夫だろうか.
いや, b = 0x7fffffff, a = INT_MINのときやはり途中結果が超えるのでアウト.
ただし処理系依存ではあるが, x86/x64をターゲットにしたC/C++コンパイラの場合, 大抵は演算結果を符号無しにキャストすれば大丈夫.
unsigned int n = (unsigned int)(b - a - 1) / s + 1;
さてステップsが負で整数がaがbよりも大きいときにも拡張してみよう. 区間(b, a]の中に a + s i(i = 0, 1, ...)の形の整数がいくつあるかということとする. つまり
n = #{x | x = a + s i, i = 0, 1, 2, ..., b < x };
この場合は
unsigned int n = (unsigned int)(a - b - 1) / (-s) + 1;
となる. 二つをくっつけて
unsigned int n = ((unsigned int)abs(a - b) - 1) / abs(s) + 1;
にするとよいだろうか.
a = bの時はn = 0になるべきだし, a > bかつs > 0のときは1になるべきだろう.
こういうのは無理してくっつけるべきではないと思う.
さてs = 0の時は計算できないのでエラーにするとして
unsigned int f(int a, int b, int s) { assert(s); if (a == b) return 0; if (a < b && s > 0) { return (unsigned int)(b - a - 1) / s + 1; } else if (a > b && s < 0) { return (unsigned int)(a - b - 1) / (-s) + 1; } return 1; }
で全部をカバーしただろうか.
実は-sが危ない. s = -2147483648に対して-sはintとして正の値にはならない.
つまり一般に標準ライブラリabs(x)はその値に対しては未定義だ.
ただ今回に限ってはその後符号無しに変換してるので結果的には動作する.
もちろん処理系依存だし, s = -2147483648でないときも上記のコードは処理系依存なので要注意.
set history save on set history size 10000 set history filename ~/.gdb_historyC++や構造体を見やすく. アセンブリ言語レベルで追いかけたい. 表示をIntel形式にしたい.
set print pretty on set print asm-demangle on set print demangle on set disassembly-flavor intel set print symbol-filename onXbyakなどで生成したコードに突入するとdisasコマンド入力しても
(gdb) disas No function contains program counter for selected frame.みたいに文句言われる. そういうときはeipで範囲指定すればいい.
disas $eip $eip+32gdbの新しめのだと
disas $eip,+32で$eipから32byteの範囲を指定することもできる.
define jit if sizeof(void*) == 8 disassemble $rip,+32 else disassemble $eip $eip+32 end endみたいにしておくと[jit]ってするだけで表示できて便利だ(名前は好きなように). レジスタ表示も
define reg if sizeof(void*) == 8 printf "rax:%016x rbx:%016x rcx:%016x rdx:%016x\nrdi:%016x rsi:%016x rbp:%016x rsp:%016x\n", $eax, $ebx, $ecx, $edx, $edi, $esi, $ebp, $esp printf " r8:%016x r9:%016x r10:%016x r11:%016x\nr12:%016x r13:%016x r14:%016x r15:%016x\n", $r8, $r9, $r10, $r11, $r12, $r13, $r14, $r15 else printf "eax:%08x ebx:%08x ecx:%08x edx:%08x\nedi:%08x esi:%08x ebp:%08x esp:%08x\n", $eax, $ebx, $ecx, $edx, $edi, $esi, $ebp, $esp end endとやってreg[return]で. おっとそもそもデフォルトではsiしても何のニーモニックを実行してるか分からないのであった. アセンブリ言語で追いかけたい時用の設定ファイルを作っておいて(たとえば.gdbasmとか)その中に
display/i $pcと書いておいて.gdbinitに
define asm source ~/.gdbasm endってすると[asm]で設定を読み込めて便利だ.
(gdb) si 0x08048975 15 Code c; (gdb) 0x08048978 15 Code c; (gdb) Code (this=0xbfaf238c) at t.cpp:5 5 Code()# display設定したとき
(gdb) si 0x08048975 15 Code c; 1: x/i $pc 0x8048975 <main+17 at t.cpp:15>: mov DWORD PTR [esp],eax (gdb) 0x08048978 15 Code c; 1: x/i $pc 0x8048978 <main+20 at t.cpp:15>: call 0x804b044 <Code at t.cpp:5> (gdb) Code (this=0xbfc581ac) at t.cpp:5
下の答え:
3番目のどちらも同じが正しい. 規格的にはそれで終了. ただWindowsやLinuxではそれ以外の部分で違いがある.
ファイルサイズを見てみよう. まず32bit Windows + VC9で見ると(A)は5632byte. (B)は, と始めると何やらうちの古いマシンのHDDが唸り出し, ビルドに時間がかかる. 結果は536876544(約512MB). うぎょ.
Linux + gcc(オプション無し)で見ると(A)は7484byte. (B)は536878412byte(約512MB)と挙動がほぼ同じ. 生成されたバイナリをobjdump -tで見てみると
// (A) 080495e0 l O .bss 20000000 buf // (B) 080495c0 g O .data 20000000 buf
と置かれている領域が異なる. .data領域はそこにそのままデータを配置するので実行ファイルのサイズに直結する. .bssはOSや実行ファイルのローダが適切に0に初期化することで実行ファイルのサイズにあまり影響は与えないということでした.
Project Eulerなどで素数列のために巨大な配列を一つ作って操作することはよくあるけどそういうときは(A)のようにした方がいい. それから組み込み系のコンパイラによっては.bss領域が0初期化されるとは限らない仕様になってたりすることもあるので注意.
// (A) static int buf[1024 * 1024 * 128]; int main() { buf[0] = 1; buf[1] = 3; buf[2] = 9; /* ここから */ }
// (B) static int buf[1024 * 1024 * 128] = { 1, 3, 9 }; int main() { /* ここから */ }答えの選択枝?
『原子力発電はいま』(プロジェクトB)
図書館でなんとなく手を取って奥付を見ると1989年発行. 当時はどういう状況だったのだろうと読んでみた. まんが内では肯定派と反対派と, そのどちらか正しいのか分からなくて悩む人が出てくる. チェルノブイリの事故の後なので, そこから漏れた放射性物質により汚染された食べ物を心配する主婦もいる. どれが正しいと結論づけてないところがいい.
何重にも防御システムを考えていて安全と思ってたのに駄目だったのは現状の通り. かと言って, 火力は石油の問題があるし, 他のクリーンエネルギーはまだまだなのは今でもあんまし変わってない.
まんがの中で反対派が「スウェーデンは2010年までに原発全廃を決めたのを日本を見倣わなければならない」, 反対派は「そんなの難しい, 90年代には見直しもあるだろう」という感じのシーン(うろ覚え)があった. ほう, では今はどうなってるのだろうと見てみるとなぜスウェーデンは原子力政策を「転換」したのか?という記事があった.
1980年に2010年までに全廃決定, 1997年に期限を撤回, 2010年立て替え承認という経緯だけを見ると原発全廃は難しくて方向転換してるように見えるけど, そうじゃないと主張されている. もちろんその反対に解釈する記事もある(スウェーデンが選ぶ 脱「脱原子力」).
いずれにせよ30年前から脱原発を進めてきたスウェーデンですら困難なのだから, 今から日本が舵を切るのは相当難しいのだろう. 改めて地道な改良の得意な日本の技術力に期待したい.
そういやboost::irangeのテストはどうしてるんだと思ってみてみたら, テストコードに
BOOST_ASSERT( (last - first) % step_size == 0 );
なんて入ってて下記(C)パターンのテストができない. つまりそんな状況は考えてない, 排除してるってことだ(ほんまかいな).
それはともかく,
boost::irangeをBoost.Parameterに対応の記事で初めてboost::irangeを知って
「これは凄い, 使ってみよう」と思った人はやめた方がいい.
見た目に騙されてないで, irange(0, 10, 3)の結果に9が入ってないのがおかしくないかと思える程度の注意力は欲しい(とえらそうなことを言う).
# 私は以前からこの挙動を知ってたので自分が知らなかったときに気づけたかどうかはわからん. ただ, 現状ではそもそも(step = 1でも)使いたいとは思わないけどね.
#include <iostream> #include <boost/range/irange.hpp> int main() { std::cout << boost::irange(0, 10) << std::endl; std::cout << boost::irange(0, 10, 2) << std::endl; std::cout << boost::irange(0, 9, 2) << std::endl; } // 結果 0123456789 ; (A) 02468 ; (B) 0246 ; (C)(A), (B)の結果を見れば大抵(C)は02468を期待する. だって普通(C)は
for (int i = 0; i < 9; i += 2)と等価なものを生成すると思うだろう. たとえばPythonなら端は含まないので
>>> print range(0,10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> print range(0,10,2) [0, 2, 4, 6, 8] >>> print range(0,9,2) [0, 2, 4, 6, 8]Haskellなら端は含むので
Prelude> [0..10] [0,1,2,3,4,5,6,7,8,9,10] Prelude> [0,2..10] [0,2,4,6,8,10] Prelude> [0,2..9] [0,2,4,6,8]どちらも首尾一貫しているのに対してboost::irangeのstep数が1より大きいときの挙動はそのどちらでもない. そんなパラメータ設定は気を付けて使うのではなく, 禁止されるべき.
float g_dummyFloat; // 下の関数を最適化させるための使わない変数 template<class T, size_t N> T my_max(const T (&x)[N]) { float dummy = g_dummyFloat; T m = x[0]; for (size_t i = 1; i < N; i++) { g_dummyFloat *= 2.0f; // 無駄に計算させてみる if (x[i] > m) m = x[i]; } // その値は使わない g_dummyFloat = dummy; return m; } int main() { const int a[] = { 1, 9, 5, 3 }; return my_max(a); } --- main PROC mov eax, 9 ret main ENDP