パスワードを忘れた? アカウント作成
14082416 journal
Perl

route127の日記: 動的型付けとsprintfの合わせ技 7

日記 by route127

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型関係あるのか。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • by hahahash (41409) on 2020年01月06日 14時35分 (#3740854) 日記

    Perlは基本的に『うまいことやってくれる』言語なので、ほとんどの場合、
    明示的にマイナスつけてないなら正の数なんだろう、みたいに扱ってくれて、
    整数の符号付き符号なしを意識しなくてもなんとかなっちゃうんですが、

    printfに関しては『C言語のprintfと同じ仕様』と決まっていて、
    %uと%dで、明示的に符号のありなしを区別する仕様になってるので、
    %dと書かれてる以上は、符号付きと解釈するしかない。

    普段うまいことやってくれてるのに、そうじゃないものが時々混ざってる。
    というPerlのつぎはぎ仕様の悪い側面じゃないかな、と思ったりします。

    • by route127 (38618) on 2020年01月06日 23時37分 (#3741290) 日記

      >『うまいことやってくれる』
      最初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.、書式指定子、印字)、

      perl5.16 %d     -2060399406
      perl5.16 %ld     -2060399406
      perl5.16 %lld     %lld(受付けない)

      perl5.24 %d      2234567890
      perl5.24 %ld     -2060399406
      perl5.24 %lld      2234567890

      となり、5.24でも長さ修飾子を適宜選択することで5.16の挙動を再現可能であった。

      %lld絡みではvim周りでも似たような話 [github.com]があったらしい。

      まあ確かにC言語(というよりPOSIXか)の知識が必要なことはちょくちょくあって、例えばPerlでTime::PieceのPOD [perldoc.jp]では網羅されてないstrptimeの書式指定子 [linuxjm.osdn.jp]なんかを調べたりした覚えがある。

      そういう低レベルPerlバッドノウハウ集とかないのだろうか。
      いやむしろそういうのは集めちゃいけない忘れるべき禁忌なのかもだ。

      親コメント
  • by Anonymous Coward on 2020年01月06日 8時08分 (#3740675)

    $ perl -v
     
    This is perl 5, version 30, subversion 1 (v5.30.1) built for x86_64-linux
    (with 30 registered patches, see perl -V for more detail)
     
    Copyright 1987-2019, Larry Wall
     
    Perl may be copied only under the terms of either the Artistic License or the
    GNU General Public License, which may be found in the Perl 5 source kit.
     
    Complete documentation for Perl, including FAQ lists, should be found on
    this system using "man perl" or "perldoc perl".  If you have access to the
    Internet, point your browser at http://www.perl.org/, the Perl Home Page.
     
    $ perl -e "$id='2234567890';$str=sprintf('%10d',$id);print $str"
    syntax error at -e line 1, near "="
    Execution of -e aborted due to compilation errors.

    こんなワンライナー、どう考えても通るわけがない。

    そこは最低でも
    perl -e '$id="2234567890";$str=sprintf("%10d",$id);print $str'
    だろうし

    むしろ素直に
    perl -e "print sprintf('%10d', '2234567890')"/code>
    でいいだろ

    • by Anonymous Coward

      わざと無知を装って、ツッコミを待っているのか?いやらしい*グロブ*め、bless()してやる。

      This is perl 5, version 24, subversion 0 (v5.24.0) built for MSWin32-x86-multi-thread-64int

      • by Anonymous Coward

        ・言語処理系とシェル/コマンドプロセッサの区別わかって書いてるの?(#3740675)
        ・perlのバイナリの区別は本質じゃない。(#3741030)

        #3740675 が un*x shellとcmd.exe のコマンドラインの扱いの違いを知らない場合、
        #3741030 のツッコミは無意味に成り果てる

        • by Anonymous Coward

          > ・perlのバイナリの区別は本質じゃない。(#3741030)

          んー、「This is perl 5, version 24, subversion 0 (v5.24.0) built for MSWin32-x86-multi-thread-64int」を引用したのは
          ツッコミを入れるには、本文の一部を引用するのが楽で効果的でいいだろと思って、
          Windows環境だと一番わかりやすいのがここかなと思ったから。
          「perlのバイナリが違うやんけ」というツッコミじゃなくて「世の中、別の環境もあるんやで」というツッコミをしたかったのよ。
          でもあんまし面白くないなこれ。

          • by Anonymous Coward

            利用者がこんなんだからperlは廃れたんだな

typodupeerror

最初のバージョンは常に打ち捨てられる。

読み込み中...