About
- NYC
Code
enum Precision < Single Double >;
`(
1 8 23
1 11 52
...
)
my Bool $debug = False;
my Int $max = 64;
class ConvertFormat {
# 将数字转换为 FatRat 格式
multi method to-fatrat($number) {
if $number ~~ Int {
FatRat.new($number,1);
} elsif $number ~~ Rat {
my @fr := self.to-fatrat-format($number);
FatRat.new(@fr[0],@fr[1]);
}
}
# 将数字转换为 能构造 FatRat 格式,即 1.001 => (1001,1000)
method to-fatrat-format($number) {
my $num = $number;
my Int $den = 1;
while $num - $num.floor > 0.0 {
$num *= 10;
$den *= 10;
}
return ($num.Int,$den);
}
# 将数组转换为 FatRat
multi method to-fatrat(Int @arr,Bool :$integer = False) {
return self.to-fatrat(+(@arr.join))
if $integer;
return FatRat.new(+(@arr.join),10 ** +@arr);
}
# 将数字转换为二进制
multi method to-binary($number where $number >= 1.0) {
my FatRat $num = self.to-fatrat(+$number.base(10));
my Int $int = $num.floor;
$num = $num - $int;
my @bin-i := self.to-binary-array($int);
my @bin-f := self.to-binary-array($num);
return self.to-fatrat(@bin-i,:integer) + self.to-fatrat(@bin-f);
}
# 将数字转换为二进制
multi method to-binary ($number where $number < 1.0) {
my FatRat $num = self.to-fatrat(+$number.base(10));
return self.to-fatrat(self.to-binary-array($num));
}
# 将小于1的浮点数转换为二进制数组,只包含小数点后的尾数
multi method to-binary-array(FatRat $number where $number < 1.0) {
return (0,) if 0.0 == $number;
my FatRat $num = $number;
my Int @bin;
while 0.0 != $num && +@bin < $max {
$num *= 2;
@bin.push: $num >= 1 ?? 1 !! 0;
if $num >= 1 {
$num -= 1;
}
}
return @bin;
}
# 将整数转换为二进制数组
multi method to-binary-array(Int $number) {
return (0,) if 0 == $number;
my Int $num = $number;
my Int @bin;
while $num > 0 {
@bin.push: $num % 2;
$num = ($num / 2).floor;
}
return @bin.reverse;
}
}
class IEEE754::Normalized {
has Int $.base; # 底数
has Int $.exponent; # 指数
has FatRat $.mantissa; # 尾数
method new ($number,Int $base = 2) {
# 数字转换为 $base 进制小数
my ($n,$e) := self.normalized(ConvertFormat.to-binary(+$number),$base);
self.bless(mantissa => $n,base => $base,exponent => $e);
}
# 规范化为 ±d.dd...d × βe,(0 ≤ d i < β) 格式的小数
method normalized(FatRat $number,Int $base) {
my FatRat $num = $number;
my Int $exp = 0;
note 'normalized: ' ~ $number ~ ' :' ~ $base
if $debug;
if $num.Int > $base {
while $num.Int >= $base {
$num /= 10;
$exp++;
}
} elsif $num < 1.0 {
while $num < 1.0 {
$num *= 10;
$exp--;
}
}
return ($num,$exp);
}
}
class IEEE754::Float {
constant $s-offset = 127;
constant $d-offset = 1023;
has Int $.sign; # 符号
has Int @.exponent; # 指数
has Int @.mantissa; # 尾数
has Precision $.prec; # 精度
has Int @.bin; # 二进制
method new ($number,:$prec) {
# 首先将数字的绝对值规格化成 ±d.dd...d × βe,(0 ≤ d i < β) 的小数
my IEEE754::Normalized $nf = IEEE754::Normalized.new(+($number.abs));
note 'NormalizedFloat -> ' ~ $nf.perl if $debug;
my (@m,@e) := self.generateBinary($nf,$prec);
# 重新整理出二进制数组形式
my @bin = (@e,@m).flat;
@bin.unshift: $number > 0 ?? 0 !! 1;
self.bless( sign => $number > 0 ?? 0 !! 1,exponent => @e,mantissa => @m,prec => $prec,bin => @bin,);
}
method generateBinary($nf,$prec) {
my $count = 0;
my @mantissa;
# 将尾数匹配出来
if $nf.mantissa ~~ /1\.@<digits> = (\d)+/ {
@mantissa.push(+$_) for @<digits>;
}
# 转换指数为二进制数组
my @exponent := ConvertFormat.to-binary-array(
$nf.exponent +
(
$prec ~~ Precision::Single ?? $s-offset !! $d-offset
)
);
if $prec ~~ Precision::Single {
$count = +@mantissa;
if $count > 23 {
@mantissa.pop for ^($count - 23);
} else {
@mantissa.unshift(0) for ^(23 - $count);
}
if +@exponent > 8 {
die("exponent overflow ...!");
}
$count = +@exponent;
@exponent.unshift(0) for ^(8 - $count);
} elsif $prec ~~ Precision::Double {
$count = +@mantissa;
if $count > 52 {
@mantissa.pop for ^($count - 52);
} else {
@mantissa.unshift(0) for ^(52 - $count);
}
if +@exponent > 11 {
die("exponent overflow ...!");
}
$count = +@exponent;
@exponent.unshift(0) for ^(11 - $count);
}
note "mantissa[{+@mantissa}] -> " ~ @mantissa if $debug;
note "exponent[{+@exponent}] -> " ~ @exponent if $debug;
return (@mantissa,@exponent);
}
method Str {
~@!bin.join;
}
}
单精度模式
multi sub MAIN(Bool :s(:single($_)),Bool :h($human) = False,Bool :debug($is-debug) = False,*@numbers) {
$debug = $is-debug;
for @numbers -> $number {
my IEEE754::Float $float = IEEE754::Float.new($number,prec => Precision::Single);
say $number ~ '[32:0] ==> ' ~ $float.Str if !$human;
say $number ~ "[32:0] ==> {$float.sign}:{$float.exponent.join}:{$float.mantissa.join}" if $human;
}
}
双精度模式
multi sub MAIN(Bool :d(:double($_)),prec => Precision::Double);
say $number ~ '[64:0] ==> ' ~ $float.Str if !$human;
say $number ~ "[64:0] ==> {$float.sign}:{$float.exponent.join}:{$float.mantissa.join}" if $human;
}
}