計算機において偉大と表現できる発明の一つに、printfがある。さまざまな処理系に実装され、現代においても誰も捨てたがらない関数。C++でiostreamとかいう劣化版(性能も悪いし使いにくい!)を推奨する変人がいたが、みんな見向きもしなかったよね。printfえらい。printf考えた人えらい。えらすぎる。
私は以前、printf(“%#x”, 0)がどう出力されるべきか悩んだことがある。前の会社にいた頃の話。%#xは0xつきで16進の文字列にする、と考えていたため、0x0と出力するのが正しいのではないかと思っていた。今でも使い勝手から考えて0x0のほうがありがたい。しかし、GNU libcや多くの処理系では0の場合は0xをつけずに出力するという挙動をしている。Linuxではdietlibcだと確か0x0だった気がする(いま手元にMacしかないのでよく分からない)。
私が調べただけでも、0x0派はPythonやGolangなどがあり、0派はPerlやRubyなどがある。
まだ調査途中だが、githubのここに置いてある(→ wtnb75/printf)。このリポジトリはいろんな言語のprintfをどう呼び出すのか、printf版のHello World集としても使えるかもしれないな。普通のHelloWorldはみんなputsみたいな感じでフォーマット文字列を使わない書き方にしちゃうから役に立たないと思うこともあり。
0x0派で問題なのは、では8進数のprintf(“%#o”, 0)は00と表示されるべきか否か、ということになるんじゃないかな。さすがに00は不自然…しかし0x0に対応すると思えば00にするのが正しいよな…というわけで、0x0派は00と出力する処理系が多いが、稀に0x0派なのに0という出力をする処理系もあったり、あるいは0o0という珍しい処理系もあった(0以外の数も0oがつき、0o12みたいになる。これはこれで理にかなっている。今のところRustのみ)。
参考情報としては、IEEEがprintfの仕様を書いていて、そこにはこうある。太字は筆者。
# Specifies that the value is to be converted to an alternative form. For o conversion, it increases the precision (if necessary) to force the first digit of the result to be zero. For x or X conversion specifiers, a non-zero result shall have 0x (or 0X) prefixed to it. For a, A, e, E, f, F, g , and G conversion specifiers, the result shall always contain a radix character, even if no digits follow the radix character. Without this flag, a radix character appears in the result of these conversions only if a digit follows it. For g and G conversion specifiers, trailing zeros shall not be removed from the result as they normally are. For other conversion specifiers, the behavior is undefined.
つまり、%#oは最初の文字が0であれば良く(00でも0でも構わない)、%#xは0以外の数値には0xをつける(つまり0の場合はつけなくてもつけても良い)、ということが書いてあるようだ。表現的にはif neccessaryとかshall haveとか気になるけど…まあこんな文書に絶対的な権威があるわけではないが。
個人的に0x0のほうが良いと思うのは、そのほうが他の出力と揃うし、逆変換(出力された文字列からフォーマット文字列と引数の形に戻す)したくなることを考えてしまうため。逆変換したいですよね皆さん。たまに思い立って書いちゃいますよね、そういうくだらないスクリプト。