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:jjj20進数として、ちゃんと桁上がりしてる。合ってるね。
実行例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 進数のネタはこれでおしまい。次回から『テキスト図形』を再開する。が、 インターバルが空くと思うので、暖かく見守ってください。。。
