エンジニアが知っておくべき「符号なし整数」をデフォルトにするべき理由
Almost Always Unsigned
Almost Always Unsigned
データ型を定義する際、基本的には「符号なし(unsigned)」の整数型を選ぶのが定石です。その理由を深掘りしてみましょう。
関連情報:自分はLEB128、Protobuf、varint、ASN.1よりも優れた可変長整数エンコーディングスキームを作ったよ。エンディアンの宣言もエンコードできるし(ちなみに符号情報はアプリケーション任せにしてある):https://github.com/pmarreck/BLIP
for (size_t i = size - 1; i < size; i--)
えーと…「できる」からといって「そうすべき」というわけじゃないよ。
それに、0以外までカウントダウンしたいときはどうするの?
少し前に議論の反対側からの関連する記事があったよ:https://news.ycombinator.com/item?id=47989154
何年経っても固定長整数の扱いにこれだけ苦労しているのは悲しいね。もちろん、未定義動作や数値forループがある低レベル言語特有の問題も多いけど、2つの数字を引いてアンダーフローが発生しうるっていうのは、一般的でありつつも少し馬鹿げてる。
記事の中で符号なし整数の差を「安全に」計算するために挙げられているコードは、符号あり整数よりは単純とはいえ、こんな儀式が必要だ:
delta = max(x, y) - min(x, y);
マジで言ってる??ただの引き算に2回も関数呼び出しが必要なのか??
なんでこういう「安全な」操作に糖衣構文を用意して、アンダーフローが多発する「普通の」操作に苦い薬のような儀式を押し付けないんだろう?
これ(2022)の記事だよね。もしHNが自動的にタイトルを適当に処理できるなら、自動的に日付も追加して(たまに間違えるとしても)いいんじゃない?DanGさんどうかな?
例えばCやC++は符号付き整数のラップを未定義にしている
ここで言いたかったのは「符号付き整数の『オーバーフロー』を未定義にしている」だと思う。ラップ(回り込み)は確定した挙動だし、オーバーフローは未定義な状況であって、選択すべき結果じゃないからね。
後の方で「Rustのこれら3つの式もLLVMのせいで無効な結果を生成する」って主張しているけど、ここはさらに混乱しているよ。
x / 0
INT_MIN / -1
INT_MAX % -1
INT_MAX - INT_MIN
INT_MAXやINT_MINをi32::MAXやi32::MIN(または好きな符号付き型)だと定義すれば、Rustで実際に起きるのは単なるパニックだよ。コンパイル時に評価される文脈で書けばコンパイルエラーになる。俺の理解では、それは「無効」というのとはちょっと違う。
Odinについても言及されているけど、詳しくないなりに思うのは、Odinもこんな無茶な処理は拒否するはず。Godboltで見ると、SIGILL(ゼロ除算)かSIGFPE(他の不可能な操作)になるみたいだし。
編集:コピペを間違えたかな?ブログに書かれていた内容と違うものもあったけど、修正しておいた。もちろん、これらはRustでは「無効」ではなく、ゼロ除算のように無意味だからパニックするものもあれば、問題なく動くものもある。どちらにせよ大した問題じゃないよ。
ストラウストラップはintよりunsignedを推奨してるし、ダイクストラもGoogleのコーディングガイドラインもintよりunsignedを推奨してる。
ブロガーはunsignedよりintを推奨してる。
どっちを信じるか悩ましいね。
Goのデフォルトのint型を任意の精度のbig intに変更しようという提案があったんだ。オーバーフローのバグは防げるけど、シリアライズされたデータの読み込み時の互換性とパフォーマンスの問題で、8年後に却下されたよ。
while(i--)を使えば0までカウントダウンできるよ。
2Dゲームを作っていて左に移動させたいときはどうすればいいの?