route127の日記: 動的型付けとsprintfの合わせ技 7
qiita眺めてたらperlのsprintfの挙動についての記事があった。
ワンライナで書くと
perl -e "$id='2234567890';$str=sprintf('%10d',$id);print $str"
というようなところだが、最後の印字出力が
-2060399406
となるというものだった。
つまり32bit符号あり整数が溢れて(2234567890-2**32)いるのではあろうが、手元のActive Perlのバージョンを5.16から5.24へ変えると発生しなかった。
元記事にも32bitのwin2000環境で発生して64bitのwin2008では発生しなかった旨記述があるが、OSというよりも処理系のコンパイル環境のような気がする。
というのも、perl -vの出力を比べるとそれぞれ
This is perl 5, version 16, subversion 3 (v5.16.3) built for MSWin32-x86-multi-thread
This is perl 5, version 24, subversion 0 (v5.24.0) built for MSWin32-x86-multi-thread-64int
であったからなのだ。
ただ、もともとのスクリプト自体にも問題はあって、$idが文字列であるのにsprintfの書式指定文字(テンプレート)が%dであったりする。
(処理系内部でSV構造体がアップグレードされてしまっている)
それ自体は動的型付け言語であるので悪いこととは言い切れないのだが、%dを%sとすれば問題を起こさずに済んだような気はする。
(あるいは書式指定文字を%uとして符合なし整数とするとか)
今回の例は動的型付けによるルーズな書き方が許される為に、より低位の問題が露出してしまったケースとでも言えばいいのだろうか。
桁あふれで思い出したがwin10の電卓アプリは128個までしか同時に開けないみたいな話があった。
char型関係あるのか。
Perl (スコア:1)
Perlは基本的に『うまいことやってくれる』言語なので、ほとんどの場合、
明示的にマイナスつけてないなら正の数なんだろう、みたいに扱ってくれて、
整数の符号付き符号なしを意識しなくてもなんとかなっちゃうんですが、
printfに関しては『C言語のprintfと同じ仕様』と決まっていて、
%uと%dで、明示的に符号のありなしを区別する仕様になってるので、
%dと書かれてる以上は、符号付きと解釈するしかない。
普段うまいことやってくれてるのに、そうじゃないものが時々混ざってる。
というPerlのつぎはぎ仕様の悪い側面じゃないかな、と思ったりします。
Re:Perl (スコア:1)
>『うまいことやってくれる』
最初bignumとか使えばうまく行くんじゃないかと思ったけどそうでもなくて結局書式指定子の仕様確かめなくちゃいけないのはうまくないような気はする…。
Perl6(Raku)も書式指定子周り [perl6.org]はあまり変わらないのか。
>printfに関しては『C言語のprintfと同じ仕様』と決まっていて、
なんかPerlも処理系依存でC言語の規格にどの程度追随してるのかまちまちな気がする。
perl5.16だとsprintfの仕様 [perldoc.jp]通り、%ld(%D)までしか受付けないものの、perl5.24にだとC99仕様のprintfの書式指定子 [linuxjm.osdn.jp]にある程度対応しているようで、書式指定子として%lld [wdic.org]を受け付けるんですよね。
この辺、Perl側でドキュメント化がされてるのか確認出来ていないが丹念にperldelta見ていけばどこかに書いてあるのかも。
ひとまず整理すると(左列から処理系ver.、書式指定子、印字)、
となり、5.24でも長さ修飾子を適宜選択することで5.16の挙動を再現可能であった。
%lld絡みではvim周りでも似たような話 [github.com]があったらしい。
まあ確かにC言語(というよりPOSIXか)の知識が必要なことはちょくちょくあって、例えばPerlでTime::PieceのPOD [perldoc.jp]では網羅されてないstrptimeの書式指定子 [linuxjm.osdn.jp]なんかを調べたりした覚えがある。
そういう低レベルPerlバッドノウハウ集とかないのだろうか。
いやむしろそういうのは集めちゃいけない忘れるべき禁忌なのかもだ。
テストして書いてんの? (スコア:0)
こんなワンライナー、どう考えても通るわけがない。
そこは最低でも
perl -e '$id="2234567890";$str=sprintf("%10d",$id);print $str'
だろうし
むしろ素直に
perl -e "print sprintf('%10d', '2234567890')"/code>
でいいだろ
Re: (スコア:0)
わざと無知を装って、ツッコミを待っているのか?いやらしい*グロブ*め、bless()してやる。
Re: (スコア:0)
・言語処理系とシェル/コマンドプロセッサの区別わかって書いてるの?(#3740675)
・perlのバイナリの区別は本質じゃない。(#3741030)
#3740675 が un*x shellとcmd.exe のコマンドラインの扱いの違いを知らない場合、
#3741030 のツッコミは無意味に成り果てる
Re: (スコア:0)
> ・perlのバイナリの区別は本質じゃない。(#3741030)
んー、「This is perl 5, version 24, subversion 0 (v5.24.0) built for MSWin32-x86-multi-thread-64int」を引用したのは
ツッコミを入れるには、本文の一部を引用するのが楽で効果的でいいだろと思って、
Windows環境だと一番わかりやすいのがここかなと思ったから。
「perlのバイナリが違うやんけ」というツッコミじゃなくて「世の中、別の環境もあるんやで」というツッコミをしたかったのよ。
でもあんまし面白くないなこれ。
Re: (スコア:0)
利用者がこんなんだからperlは廃れたんだな