* perl の bignum [#e9304035]
最近,時刻やストレージの容量など,32 bit over の整数を扱わざるをえない機会が多くなってきている.
もちろん,( 32 bit CPU 上での ) 素の perl でも 2**32 を越える整数を扱うことはできるが,内部的には浮動小数点で保持しているはずであり,何かの機会にひょっこり誤差が出てきそうで気味が悪い.
こういうときに多桁整数演算用のパッケージ Math::BitInt がある.
が,これは純粋に perl クラスで実装されており,単純に足し算するだけでも
#!/usr/bin/perl
use Math::BigInt;
$a = Math::BigInt->new ( '1' ); # $a = 1;
$b = Math::BigInt->new ( '2' ); # $b = 2;
$a->badd( $b ); # $a += $b;
printf "%s\n", $a->bstr();
のような表記になり,ちょっと使いづらい.
あと,bignum というクラス(?)もある.
#!/usr/bin/perl
use bignum;
printf "%s\n", 1+2;
内部的には Math::BigInt と Math::BigFloat を使用しているそうであるが,これらとの大きな違いは
- 数値定数を全て Math::BigInt or Math::BigFloat のインスタンスとして扱う
- 演算子もオーバロードされる
というわけで,プログラムの先頭に use bignum; と宣言するだけで,今までのプログラムが多桁演算対応に早変わりしてくれる.
もちろん,演算速度は犠牲になるが,それは多桁演算とのトレードオフというものである.
が,この bignum,妙な癖があるようで
#!/usr/bin/perl
package Foo;
use bignum;
sub new {
my $class = shift;
my $self = {};
my %param = @_;
$self->{name} = $param{name};
bless $self, $class;
}
sub set {
my $self = shift;
my %kv = @_;
while ( my ( $k, $v ) = each %kv ) {
$self->{hash}->{$k} = $v;
}
1;
}
sub show {
my $self = shift;
my $kv = $self->{hash};
printf "--- %s ---\n", $self->{name};
my %kloop = %$kv;
while ( my ( $k, $v ) = each %kloop ) {
$v = 'UNDEF' if !defined $v;
print "$k => $v\n";
}
1;
}
sub DESTROY {
my $self = shift;
show ( $self );
1;
}
1;
#---
$foo = Foo->new( name => 'explicit' );
$foo->set ( 1 => 2, 3 => 4, 5 => 6 );
$foo->show();
undef $foo;
$foo = Foo->new( name => 'implicit' );
$foo->set ( 1 => 2, 3 => 4, 5 => 6 );
$foo->show();
exit;
このプログラムを実行してみると…
--- explicit ---
1 => 2
3 => 4
5 => 6
--- explicit ---
1 => 2
3 => 4
5 => 6
--- implicit ---
1 => 2
3 => 4
5 => 6
--- implicit ---
1 => UNDEF
3 => 4
5 => 6
exit より後では,インスタンスが一部消滅してしまうらしい.
use bignum; の行をコメントアウトして実行すると,もちろん
--- explicit ---
1 => 2
3 => 4
5 => 6
--- explicit ---
1 => 2
3 => 4
5 => 6
--- implicit ---
1 => 2
3 => 4
5 => 6
--- implicit ---
1 => 2
3 => 4
5 => 6
期待した通りの結果が得られる.
うーん.