[Perl 6]IEEE754

前端之家收集整理的这篇文章主要介绍了[Perl 6]IEEE754前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

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 &amp;&amp; +@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;
}

}

猜你在找的Perl相关文章