Hatena::ブログ(Diary)

分室の分室 このページをアンテナに追加

2012-01-07 Sat

【182】 10進数を 2〜36進数に変換…

意外と長文になってしまった…

 以前…

     2011-07-10 Sun
    【107】小ネタ(10進数を 2進〜16進数に変換)
     http://d.hatena.ne.jp/foussin/20110710/1310303775

…という記事を掲載した。

で、前回記事は、NHK BSプレミアムの『コズミックフロント』の番組で…


    マヤ暦では『20進数によるロングカウント』を使って日数を記録した


…ということを知り、【107】の記事で紹介した『dec2hex.pl』を 20進数対応に
しよう…と思った。現実に『20進数』を使っていた文明が存在したのなら、やっ
て然るべきと思った次第。もちろん、20進数といっても数値演算はできない。
【追記:上記の日本語もちょっと変だけど、面倒なので放置…】

 プログラミングで使用可能な数値リテラルは、相変わらず…

    10進数 (0 1 2 3 4 5 6 7 8 9)
     8進数 (0 1 2 3 4 5 6 7)
     2進数 (0 1)
    16進数 (0 1 2 3 4 5 6 7 8 9 a b c d e f)

…に限定される。これらを Perl で使用する場合…

    10進数 ....... 15
     8進数 ...... 017   (先頭に 0  を付けると 8進数と判断する)
     2進数 ... 0b1111   (先頭に 0b を付けると 2進数と判断する)
    16進数 ...... 0xf   (先頭に 0x を付けると16進数と判断する)

…このように記述すれば良い(上記は10進数の『15』を表している)。例えば、

    $sum =  15 + 017;       # 30
    $sum = 0xf + 0b1111;    # 30

…このように記述できる。

			

20進数とか36進数をどうやって表現する?

 16進数では、0-9a-f を使って 1桁を表現している。それに倣って、20進数で は、さらに g h i j を使えばいーんでねーの? と、そう思った。と、くれば… アルファベットは 26文字ある。 数字(0-9) と併せれば『36進数』まで表現できる。 …さすがに36進数ってのは、ネタに過ぎないけど、一応やってみた。

dec2hex.pl ver0.30 のソース

#!/usr/bin/perl

use strict;
use warnings;
package dec2hex;

our $str = "";

# 元ネタはこちら↓######################################################
#
# 文字コード「超」研究     深沢千尋 著     株式会社ラトルズ・刊
# p.193 の掲載スクリプト "dec2hex.pl" ―― 10進数を16進数に変換
# (現在、改訂版が発売されているので、このページ数は変わっているかも…)
#
# 深沢千尋サポートページ
# url: http://query1000.com/
#
#(株)ラトルズ刊『文字コード【超】研究 改訂第2版』公式サポートページ
# url: http://query1000.com/chrc/
#
# url: http://query1000.com/chrc1/ (初版 [改訂前] のサポートページ)
#
########################################################################

# ----------------------------------------------------------------------
# 参考文献の掲載スクリプトをライブラリに改造
# ----------------------------------------------------------------------
# arranged by pablo foussin
#        ver0.00   (2005.05.26 Thu 21:05 - 22:55)
# $VERSION='0.10'; (2005.05.27 Fri 00:07 - 2005.05.27 Fri 13:02)
# $VERSION='0.20'; (2012.01.06 Fri 01:02) # 20進数まで対応
# $VERSION='0.30'; (2012.01.07 Sat 01:49) # 36進数まで対応
#
# Usage1: require 'dec2hex.pl';
#         $hexadeci = &dec2hex::func( [$opt,] $decimal );  # scalar
#         @hex_list = &dec2hex::func( [$opt,] @dec_list ); # list
#
# Usage2: dec2hex.pl [-opt] decimal [decimal ... ]
#         引数(decimal)には 10進数を指定する
#
#         -opt(オプション)は -t2 〜 -t36 の範囲で指定する
#         範囲外の数値を指定すると、-t16 がセットされる
#         オプションを省略すると、デフォルトとして -t16 がセットされる

if ($0 =~ /dec2hex\.pl/) {      # command
    my @kotae = &func(@ARGV);
    print $str;                 # グローバル変数
    foreach my $ko (@kotae) { print $ko, "\n"; }
}

sub func {
    my (@dec, @hex_table, $dec, $kotae, $jou, @kotae);
    my @param = @_;
    my $tei   = "";
    foreach my $p (@param) {
        if ($p =~ /^\-t(\d*)$/) {
            $tei = $1;
            $tei        || ($tei = 16); # default
            ($tei <  2) && ($tei = 16); # 底は 2以上でなければならない
            ($tei > 36) && ($tei = 16); # 底は36以下でなければならない
        } else {
            $p =~ tr/0-9//cd;
            $p or $p = 0;
            push(@dec, $p);
        }
    }
    $tei || ($tei = 16);                # opt省略時は、底として16を使う
    @hex_table  = ('0' .. '9', 'a' .. 'z');              # 変換テーブル
    my @table   = @hex_table[0 .. $tei - 1];
    $str        = "chr: @table\n";      # グローバル変数
    my @prefix  = ("", "", "0b", "3:", "4:", "5:", "6:", "7:", "0",
                   "9:", "", "11:", "12:", "13:", "14:", "15:", "0x",
                   "17:", "18:", "19:", "20:", "21:", "22:", "23:",
                   "24:", "25:", "26:", "27:", "28:", "29:", "30:",
                   "31:", "32:", "33:", "34:", "35:", "36:");   # 接頭語
    foreach $dec (@dec) {
        $kotae  = "";                   # 初期化
        $jou    = 0;                    # 底の何乗か。最初は 0
        while ($dec >= $tei) {
          # $dec が底未満になるまで入力された数を底で割った余
            my $mod = $dec % $tei;
          # 入力された数を底で割った商
            $dec    = ($dec - $mod) / $tei;
          # 答の前に1桁目の結果を追加
            $kotae  = $hex_table[$mod] . $kotae;
            ++$jou;                     # 底の指数をインクリメント
        }
        $kotae = $prefix[$tei] . $hex_table[$dec] . $kotae;
        push(@kotae, $kotae);
    }
    return(wantarray ? @kotae : $kotae[0]);
}

1;
__END__

 ソース内のコメント文に書いてある通り、このスクリプトには元ネタがある。
『はてな』では自動的に url がリンクされる。 興味がある人は覗いてみては ?

 筆者(foussin) とは何の関係もありませんが、深沢千尋氏の書籍は、けっこう
面白いのでオススメします。

実行例1(コマンドとして実行)

 例えば、10進数の 255 を n進数に変換する例は…
c:\usr\edit>dec2hex.pl -t2 255 chr: 0 1 0b11111111 c:\usr\edit>dec2hex.pl -t8 255 chr: 0 1 2 3 4 5 6 7 0377 c:\usr\edit>dec2hex.pl -t16 255 chr: 0 1 2 3 4 5 6 7 8 9 a b c d e f 0xff c:\usr\edit>dec2hex.pl -t20 255 chr: 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j 20:cf c:\usr\edit>dec2hex.pl -t36 255 chr: 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z 36:73 c:\usr\edit>
 こんな感じになる。 2進数、8進数、16進数には、それぞれ "0b", "0", "0x" と固有の接頭語が存在するが、20進数とか36進数にはそれがないので、苦肉の策 として "nn:" という接頭語を勝手に付けることにした。  しかし、20進数とか 36進数なんて、ふだんは使わないので、本当に "20:cf" で合ってるのか心配になる。そんな時は… my $tei = 20; for (1..9) { print "$_: ", $tei ** $_, "\n"; }  上のスクリプトを実行すると、こうなる…↓ 1: 20 # 20進数 1桁で 0〜 19 までを表現できる 2: 400 # 20進数 2桁で 0〜399 までを表現できる 3: 8000 # 以下同様… 4: 160000 5: 3200000 6: 64000000 7: 1280000000 8: 25600000000 9: 512000000000 …で、、、
c:\usr\edit>dec2hex.pl -t20 8000 chr: 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j 20:1000 c:\usr\edit>dec2hex.pl -t20 7999 chr: 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j 20:jjj
 20進数として、ちゃんと桁上がりしてる。合ってるね。

実行例2(外部関数として実行)

 次は、dec2hex.pl を require で読み込んでみる。例えばのサンプル。↓

#!/usr/bin/perl

# test-d2hex.pl (dec2hex.pl v0.30 用のサンプルスクリプト)
#
# 書式:test-d2hex.pl [n進数]
# 引数:2 〜 36 の範囲(デフォルトは 16)

use strict;
use warnings;
require 'dec2hex.pl';

my @dec = ();
my @hex = ();
my $tei = $ARGV[0];
$tei or $tei = 16;
my $opt = "-t$tei";

for (1..8) {
    push(@dec, ($tei ** ($_ - 1)), ($tei ** $_ - 1));
}
@hex = dec2hex::func($opt, @dec);

# 10進数の桁揃え (n進数表示は最大8桁+接頭語 = 11桁に決まっている)
my $gt  = $tei ** 8;
my $len = length($gt) + 1;

my $ct = 1;
while (@hex) {
    my $dz = shift @dec;
    my $d0 = shift @dec;
    my $xz = shift @hex;
    my $x0 = shift @hex;
    printf " % ${len}u : % 11s (${tei}進数 ${ct}桁)\n", $dz, $xz;
    printf " % ${len}u : % 11s (${tei}進数 ${ct}桁)\n", $d0, $x0;
    ++$ct;
}

__END__

 実行例。↓
c:\usr\edit>test-d2hex.pl 2 1 : 0b1 (2進数 1桁) 1 : 0b1 (2進数 1桁) 2 : 0b10 (2進数 2桁) 3 : 0b11 (2進数 2桁) 4 : 0b100 (2進数 3桁) 7 : 0b111 (2進数 3桁) 8 : 0b1000 (2進数 4桁) 15 : 0b1111 (2進数 4桁) 16 : 0b10000 (2進数 5桁) 31 : 0b11111 (2進数 5桁) 32 : 0b100000 (2進数 6桁) 63 : 0b111111 (2進数 6桁) 64 : 0b1000000 (2進数 7桁) 127 : 0b1111111 (2進数 7桁) 128 : 0b10000000 (2進数 8桁) 255 : 0b11111111 (2進数 8桁) c:\usr\edit>test-d2hex.pl 8 1 : 01 (8進数 1桁) 7 : 07 (8進数 1桁) 8 : 010 (8進数 2桁) 63 : 077 (8進数 2桁) 64 : 0100 (8進数 3桁) 511 : 0777 (8進数 3桁) 512 : 01000 (8進数 4桁) 4095 : 07777 (8進数 4桁) 4096 : 010000 (8進数 5桁) 32767 : 077777 (8進数 5桁) 32768 : 0100000 (8進数 6桁) 262143 : 0777777 (8進数 6桁) 262144 : 01000000 (8進数 7桁) 2097151 : 07777777 (8進数 7桁) 2097152 : 010000000 (8進数 8桁) 16777215 : 077777777 (8進数 8桁) c:\usr\edit>test-d2hex.pl 16 1 : 0x1 (16進数 1桁) 15 : 0xf (16進数 1桁) 16 : 0x10 (16進数 2桁) 255 : 0xff (16進数 2桁) 256 : 0x100 (16進数 3桁) 4095 : 0xfff (16進数 3桁) 4096 : 0x1000 (16進数 4桁) 65535 : 0xffff (16進数 4桁) 65536 : 0x10000 (16進数 5桁) 1048575 : 0xfffff (16進数 5桁) 1048576 : 0x100000 (16進数 6桁) 16777215 : 0xffffff (16進数 6桁) 16777216 : 0x1000000 (16進数 7桁) 268435455 : 0xfffffff (16進数 7桁) 268435456 : 0x10000000 (16進数 8桁) 4294967295 : 0xffffffff (16進数 8桁) c:\usr\edit>test-d2hex.pl 20 1 : 20:1 (20進数 1桁) 19 : 20:j (20進数 1桁) 20 : 20:10 (20進数 2桁) 399 : 20:jj (20進数 2桁) 400 : 20:100 (20進数 3桁) 7999 : 20:jjj (20進数 3桁) 8000 : 20:1000 (20進数 4桁) 159999 : 20:jjjj (20進数 4桁) 160000 : 20:10000 (20進数 5桁) 3199999 : 20:jjjjj (20進数 5桁) 3200000 : 20:100000 (20進数 6桁) 63999999 : 20:jjjjjj (20進数 6桁) 64000000 : 20:1000000 (20進数 7桁) 1279999999 : 20:jjjjjjj (20進数 7桁) 1280000000 : 20:10000000 (20進数 8桁) 4294967295 : 20:jjjjjjjj (20進数 8桁) c:\usr\edit>test-d2hex.pl 36 1 : 36:1 (36進数 1桁) 35 : 36:z (36進数 1桁) 36 : 36:10 (36進数 2桁) 1295 : 36:zz (36進数 2桁) 1296 : 36:100 (36進数 3桁) 46655 : 36:zzz (36進数 3桁) 46656 : 36:1000 (36進数 4桁) 1679615 : 36:zzzz (36進数 4桁) 1679616 : 36:10000 (36進数 5桁) 60466175 : 36:zzzzz (36進数 5桁) 60466176 : 36:100000 (36進数 6桁) 2176782335 : 36:zzzzzz (36進数 6桁) 2176782336 : 36:1000000 (36進数 7桁) 4294967295 : 36:zzzzzzz (36進数 7桁) 4294967295 : 36:10000000 (36進数 8桁) 4294967295 : 36:zzzzzzzz (36進数 8桁) c:\usr\edit>
 最後の36進数 7桁以降の整数値の表示が変なのは、符号無し整数の有効範囲を 超えたためらしい。 printf で "% ${len}u" ではなく、f(float) で小数点以下 を除外して出力すれば直るかな…。  n 進数のネタはこれでおしまい。次回から『テキスト図形』を再開する。が、 インターバルが空くと思うので、暖かく見守ってください。。。