2006年03月13日

CInt とオーバーフロー

今回はちょっとシリーズを離れてオーバーフローのお話です。なんか似た話題を以前にやった気もしますが。

アンマネージドとやりとりしていると、異なる数値型間の変換が必要になる場面が出てきます。今回 Task クラスの実装を書いている場面でも、それが必要になる場面がありました。

アンマネージドとは DWORD つまり UInt32 でやりとりしますが、そのままでは意味を取りづらいので TimeSpan 型としてプロパティを定義し、内部で Int32 に変換するというやり方です。DWORD を あえて Int32 で書くのはまあ CLS 準拠を目指すための癖みたいなもんです。今考えるとこれが悩みの元になったわけなんですが。

そのアンマネージドのメソッドは 0 から 0xFFFFFFFFL まで受け入れるため、TimeSpan.TotalMilliseconds を 一旦 Int64 にキャストし、最大値チェックを行った後 Int32 にキャストし直そうという算段でした。

unchecked を使える C# はともかく、VB.NET での Int64 から Int32 へのキャストは CInt か Convert.ToInt32 になりますが、この二つはいずれもオーバーフローを抑止するオプションが存在しないため、変換元の値を確認して &H80000000L 以上だったら &H100000000L を引く、そして変換ってのがわかりやすい手段です。

この過程で、何を思ったか Int64 に And &HFFFFFFFFL の演算をしてやればどうだろう? という思考にたどり着きました。確かに 32bit にはなりますがだからって CInt したときオーバーフローするのには変わらないのに。

ところがどっこい、これが何故かエラーが出なかったのです。もとの Int64 に &HFFFFFFFEL が入っていた場合、CInt した結果普通に -2 になります。気持ち悪。ちなみに C# でも checked ステートメント内で同じ結果になります。

IL レベルでも問題なく conv.ovf.i4 が発行されているため、さっぱり原因がつかめません。いい加減煮詰まったので 2ch で聞いてみたら、いくつかやりとりがあった末に こういう結論に。つまり定数が 32bit で収まること、また And 演算のため計算結果も必ず 32 bit 以下になることから、定数の下位 32bit で演算するように最適化する。そのとき正しくは通るべきオーバーフローチェックがパスされてしまう、と。また Mono では普通にオーバーフローするらしいです。つきあってくださった方々、改めてありがとうございました。

And &HFFFFFFFFL 後の CInt が決してオーバーフローしないのなら利用価値はありますが、x64 版でどうなるか知れたものじゃないし Mono でも普通にオーバーフローするとなると使ってもバグの元になるだけですので封印封印。

まあそう言う顛末の話でした。

しかし VB.NET にもオーバーフローの制御とか三項演算子とか欲しいんですが。IIf は Object でのやりとりになるから IIf(Of T) があればいいんだけどなー。



posted by Hongliang at 21:55| Comment(0) | TrackBack(1) | VB.NET | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック

re: オーバーフローチェック (Visual Basic)
Excerpt: re: オーバーフローチェック (Visual Basic)
Weblog: とりこびと ぶろぐ。
Tracked: 2007-03-28 17:13

ここ(hongliang.seesaa.net)で公開しているものについて、利用は自由に行って頂いて構いません。改変、再頒布もお好きになさって下さい。利用に対しこちらが何かを要求することはありません。

ただし、公開するものを使用、または参考したことによって何らかの損害等が生じた場合でも、私はいかなる責任も負いません。

あ、こんなのに使ったってコメントを頂ければ嬉しいです。