Perl では、整数、小数、指数、8進数、16進数などの数値を使うことができます。
$a1 = 12345; # 整数 $a2 = -12345; # 負の数 $a3 = 123.45; # 小数 $a4 = 123E45; # 指数(123×10の45乗) $a5 = 123e45; # 指数(123×10の45乗) $a6 = 0777; # 8進数(0777 は 10進数の511) $a7 = 0x7f; # 16進数(0x7f は 10進数の127) $a8 = 1_234_567; # $a8 = 1234567; と同じ
指数の E は大文字でも小文字でも構いません。0 で始まる数値は 8進数、0x で始まる数値は 16進数と解釈されます。数値中のアンダーバー(_)は無視されます。カンマ(,)の代用として 3桁毎に _ を入れることで、数値の桁を読みやすくするのに役立ちます。
0~7 の 8個の数字を用いて表すのが 8進数 です。0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 20, ... のように増えていきます。8進数の 123 は、1×8×8+2×8+3=83 で、10進数の 83 に相当します。(→ 8進数を扱う)
16個の数字を用いて表すのが 16進数 です。16個の数字には 0~9 に加えて a~f の文字を使用します。0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, 10, 11, ... のように増えていきます。16進数の 123 は、1×16×16+2×16+3=291 で、10進数の 291 に相当します。(→ 16進数を扱う)
小数部を切捨てるには int() を用います。int には「整数(Integer)にする」という意味があります。
$a1 = 1234.56; $a2 = int($a1); print "a1=$a1, a2=$a2\n";
実行結果は次のようになります。
a1=1234.56, a2=1234
引数を省略すると、省略時変数 $_ を対象とします。
$n = int($a); $n = int(); # int($_) と同じ意味
数値を 100 の位で切り捨てるには、数値を一度 1000 で割り、それを int() で整数化した後に 1000 倍します。
$a1 = 1234.56; $a2 = int($a1 / 1000) * 1000; print "a1=$a1, a2=$a2\n";
実行結果は次のようになります。
a1=1234.56, a2=1000
同様に、0.01 の位で切り捨てるには、数値を 10 倍して int() したものを 10 で割ります。
$a1 = 1234.56; $a2 = int($a1 * 10) / 10; print "a1=$a1, a2=$a2\n";
小数以下を切り上げるには、小数部を切り捨てた値に 1 を足します。ただし、小数部が 0 の時には 1 を足してはならないので、ちょっと処理は複雑になります。下記のスクリプトでは、int($a1) が $a1 に等しければそのまま $a1 を、さもなくば int($a1) に 1 を足した値を $a2 に代入しています。
$a1 = 1234.56; $a2 = (int($a1) == $a1) ? $a1 : int($a1) + 1; print "a1=$a1, a2=$a2\n";
実行結果は次のようになります。
a1=1234.56, a2=1235
「式 ? 値1 : 値2」の使用法については 条件演算子 を参照してください。
小数第1位で四捨五入するには、0.5 を足して小数部を切り捨てます。
$a1 = 1234.56; $a2 = int($a1 + 0.5); # 小数第1位で四捨五入する print "a1=$a1, a2=$a2\n";
実行結果は次のようになります。
a1=1234.56, a2=1235
100 の位で四捨五入するには、1000 で割って 0.5 を足した後、小数部を切り捨てて、1000 倍します。
$a1 = 1234.56; $a2 = int($a1 / 1000 + 0.5) * 1000; # 100の位で四捨五入する print "a1=$a1, a2=$a2\n";
rand(n) は、0 以上 n 未満の乱数を返します。rand() で求めた値を int() で整数化することにより、0 以上 n 未満の整数値を得ることができます。
for ($i = 0; $i < 3; $i++) { $xx = int(rand(100)); print "xx=$xx\n"; }
実行結果の例を以下に示します。
xx=25 xx=46 xx=53
Perl 5.004 よりも古いバージョンでは、rand() を用いるプログラムの先頭で 1度だけ srand() を呼び出す必要があります。srand() を呼び出さない場合、プログラムはいつも同じ数字から始まって、同じ順序の乱数を生成してしまいます。
srand(); for ($i = 0; $i < 3; $i++) { $xx = int(rand(100)); print "xx=$xx\n"; }
srand(num) の num には乱数の初期値の元となる数値を指定します。num が同じだと、同じ順序の乱数が生成されてしまいます。省略時は time() の値が使用されるので起動した時刻により別の順序の乱数が生成されますが、悪意のあるハッカーには、time() の値を予測して乱数の結果も予測されてしまいますので、ちょっと危険です。UNIX でしか使用できませんが、プロセス番号($$)を用いて例えば次のようにするとかなり安全になります。
srand(time() ^ ($$ + ($$ << 15)));
12,345,678.9999 のように数値を 3桁区切りで表示にしたい場合、下記のようにします。
$num = 12345678.9999; while ($num =~ s/^([-+]?\d+)(\d{3})/$1,$2/) { } print "$num\n";
実行結果は次のようになります。
12,345,678.9999
何をやっているのかよくわからないマニアックなスクリプトですが、12345678 を 12345,678 に置換、12345 を 12,345 に置換、という処理を、置換するものが無くなるまで繰り返しています。詳細は 正規表現 や 置換 を参照してください。
桁区切りされた数値を解釈するには、置換のテクニックですべてのカンマ(,)を取り除きます。
$num = "12,345,678.9999"; $num =~ s/,//g; print "$num\n";
10進数の数値を 16進数の文字列に変換するには sprintf() の %x を用います。逆に、16進数の文字列を 10進数の数値に変換するには hex() を用います。数値同士であれば特に変換は不要です。
# 10進数(数値)から16進数(文字列)への変換 $hex = sprintf("%02x", 255); # $hex = "ff" print "hex = $hex\n"; # 16進数(文字列)から10進数(数値)への変換 $dec = hex("ff"); # $dec = 255 print "dec = $dec\n"; # 16進数(数値)から10進数(数値)への変換 $dec = 0xff; # $dec = 255 print "dec = $dec\n";
実行結果は次のようになります。
hex = ff dec = 255 dec = 255
10進数の数値を 8進数の文字列に変換するには sprintf() の %o を用います。逆に、8進数の文字列を 10進数の数値に変換するには oct() を用います。数値同士であれば特に変換は不要です。
# 10進数(数値)から8進数(文字列)への変換 $oct = sprintf("%03o", 255); # $oct = "377" print "oct = $oct\n"; # 8進数(文字列)から10進数(数値)への変換 $dec = oct("377"); # $dec = 255 print "dec = $dec\n"; # 8進数(数値)から10進数(数値)への変換 $dec = 0377; # $dec = 255 print "dec = $dec\n";
実行結果は次のようになります。
oct = 377 dec = 255 dec = 255
10進数の数値を 2進数の文字列に変換するには sprintf() の %b を用います。ただし、古い perl のバージョンではサポートしていません。
# 10進数(数値)から2進数(文字列)への変換 $bin = sprintf("%08b", 255); # $bin = "11111111" print "bin = $bin\n";
printf() で %b を使用できない場合は、下記のようなサブルーチンを用いると便利です。第2引数には桁数を指定します。
print NumToBin(0x5555, 16) . "\n"; # 数値から2進数文字列への変換 # NumToBin(数値, 桁数) sub NumToBin { local($num, $fig) = @_; local($bin); do { $bin = ($num % 2) . $bin; } while ($num = int($num / 2)); return "0" x ($fig - length($bin)) . $bin; }
hex() や oct() と同様に bin() があればよいのですが、現在の Perl には実装されていません。仕方ないので、下記のようなサブルーチンを用います。
printf("%x\n", BinToNum("0101010101010101")); # 2進数文字列から数値への変換 # BinToNum(2進数文字列) sub BinToNum { local($bin) = @_; local($dec); $bin =~ s/(.)/{ $dec *= 2; if ($1) { $dec += 1; } }/eg; return $dec; }
sin() や cos() など、いくつかの数学的演算用の関数が定義されています。
$pi = 3.141592653589793; # 円周率 $r = 100; # 半径 $a = 30; # 角度 30° $x = $r * cos($pi * $a / 180); # X座標を求める $y = $r * sin($pi * $a / 180); # Y座標を求める $a = atan2($y, $x) / $pi * 180; # 座標 X, Y の角度を求める printf("x=%5.2f, y=%5.2f, a=%d\n", $x, $y, $a);
これを実行すると次のようになります。
x=86.60, y=50.00, a=29
数学関数には次のようなものがサポートされています。
関数 | 説明 |
---|---|
sqrt(x) | x の平方根。 |
sin(x) | sin x。x は180度をπとするラジアン単位で指定する。 |
cos(x) | cos x。x は180度をπとするラジアン単位で指定する。 |
atan2(y, x) | atan y/x。座標から角度を求める際に使用される。 |
exp(n) | e の n 乗。 |
log(x) | e を基とする x の log。 |
abs(x) | x の絶対値。(Perl5~) |
コンピュータ内部では小数を2進数で保持しています。0.5 であれば 1/2 のように正確に表現することができるのですが、0.1 は 1/16 + 1/32 + 1/256 + 1/512 + ... のような近似値として表現します。この際、0.1 は正確な 0.1 ではないといった、丸め誤差 の問題が生じます。例えば、
$num = 0.0; for ($i = 1; $i <= 20; $i++) { $num += 0.1; if ($num * 10 != $i) { print "$num × 10 は $i ではありません。\n"; } }
を実行すると 0.3 が正確な 0.3 ではなくなるために、例えば次のように表示されてしまいます。
0.3 × 10 は 3 ではありません。 0.8 × 10 は 8 ではありません。 1 × 10 は 10 ではありません。 :
丸め誤差の影響を少なくするには、誤差が蓄積してしまう累計処理をかけ算に置きかえたり、有効桁で四捨五入するなどの改善を行います。
$num = 0.0; for ($i = 1; $i <= 20; $i++) { $num = 0.1 * $i; # 累計をかけ算に変更 $num = int(($num * 10) + 0.5) / 10; # 有効桁で四捨五入 if ($num * 10 != $i) { print "$num × 10 は $i ではありません。\n"; } }
10桁を超える巨大な整数を扱うと 1e+015 などの指数付きで表現されてしまったり、丸め誤差の問題が発生したりします。Perl で 10桁を超える整数を扱う場合は BigInt モジュールを使用すると便利です。
use Math::BigInt; $n1 = Math::BigInt->new("123456789012345678901234567890"); $n2 = $n1 * 10000; $n3 = $n2 + 1; printf("%s\n", $n3);
結果は次のようになります。
+1234567890123456789012345678900001
new(...) の ... 部分で値を渡すときや、値を参照する時は数値ではなく、文字列として扱ってください。下記の例では値を数値で渡しているために結果が NaN となってしまいます。
use Math::BigInt; $n1 = Math::BigInt->new(123456789012345678901234567890); print "$n1\n";
BigInt は Perl の通常の整数ではありませんが、+ や <=> などの演算子がオーバーライドされているため、通常の整数のように数値演算子を用いることができます。文字列として比較することも可能です。また、badd()、bsub()、bnum()、bdiv()、bcmp() などの演算メソッドも用意されています。
use Math::BigInt; $n1 = Math::BigInt->new("123456789012345678901234567890"); $n2 = $n1 + 1; if ($n1 == $n2) { print "等しい\n"; } if ($n1 eq $n2) { print "等しい\n"; } if ($n1->bcmp($n2) == 0) { print "等しい\n"; }