[Perl 6]IEEE754

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

About

  1. - NYC

Code

  1. enum Precision < Single Double >;

  2. `(

  3. 1 8 23
  4. 1 11 52
  5. ...
  6. )

  7. my Bool $debug = False;

  8. my Int $max = 64;

  9. class ConvertFormat {

  10. # 将数字转换为 FatRat 格式
  11. multi method to-fatrat($number) {
  12.     if $number ~~ Int {
  13.         FatRat.new($number,1);
  14.     } elsif $number ~~ Rat {
  15.         my @fr := self.to-fatrat-format($number);
  16.         FatRat.new(@fr[0],@fr[1]);
  17.     }
  18. }
  19. # 将数字转换为 能构造 FatRat 格式,即 1.001 => (1001,1000)
  20. method to-fatrat-format($number) {
  21.     my $num = $number;
  22.     my Int $den = 1;
  23.     while $num - $num.floor > 0.0 {
  24.         $num *= 10;
  25.         $den *= 10;
  26.     }
  27.     return ($num.Int,$den);
  28. }
  29. # 将数组转换为 FatRat
  30. multi method to-fatrat(Int @arr,Bool :$integer = False) {
  31.     return self.to-fatrat(+(@arr.join))
  32.         if $integer;
  33.     return FatRat.new(+(@arr.join),10 ** +@arr);
  34. }
  35. # 将数字转换为二进制
  36. multi method to-binary($number where $number >= 1.0) {
  37.     my FatRat $num = self.to-fatrat(+$number.base(10));
  38.     my Int $int = $num.floor;
  39.     $num = $num - $int;
  40.     my @bin-i := self.to-binary-array($int);
  41.     my @bin-f := self.to-binary-array($num);
  42.     return self.to-fatrat(@bin-i,:integer) + self.to-fatrat(@bin-f);
  43. }
  44. # 将数字转换为二进制
  45. multi method to-binary ($number where $number < 1.0) {
  46.     my FatRat $num = self.to-fatrat(+$number.base(10));
  47.     return self.to-fatrat(self.to-binary-array($num));
  48. }
  49. # 将小于1的浮点数转换为二进制数组,只包含小数点后的尾数
  50. multi method to-binary-array(FatRat $number where $number < 1.0) {
  51.     return (0,) if 0.0 == $number;
  52.     my FatRat $num = $number;
  53.     my Int @bin;
  54.     while 0.0 != $num &amp;&amp; +@bin < $max {
  55.         $num *= 2;
  56.         @bin.push: $num >= 1 ?? 1 !! 0;
  57.         if $num >= 1 {
  58.             $num -= 1;
  59.         }
  60.     }
  61.     return @bin;
  62. }
  63. # 将整数转换为二进制数组
  64. multi method to-binary-array(Int $number) {
  65.     return (0,) if 0 == $number;
  66.     my Int $num = $number;
  67.     my Int @bin;
  68.     while $num > 0 {
  69.         @bin.push: $num % 2;
  70.         $num = ($num / 2).floor;
  71.     }
  72.     return @bin.reverse;
  73. }
  74. }

  75. class IEEE754::Normalized {

  76. has Int $.base; # 底数

  77. has Int $.exponent; # 指数

  78. has FatRat $.mantissa; # 尾数

  79. method new ($number,Int $base = 2) {
  80.     # 数字转换为 $base 进制小数
  81.     my ($n,$e) := self.normalized(ConvertFormat.to-binary(+$number),$base);
  82.     self.bless(mantissa => $n,base => $base,exponent => $e);
  83. }
  84. # 规范化为 ±d.dd...d × βe,(0 ≤ d i < β) 格式的小数
  85. method normalized(FatRat $number,Int $base) {
  86.     my FatRat $num = $number;
  87.     my Int $exp = 0;
  88.     note 'normalized: ' ~ $number ~ ' :' ~ $base
  89.         if $debug;
  90.     if $num.Int > $base {
  91.         while $num.Int >= $base {
  92.             $num /= 10;
  93.             $exp++;
  94.         }
  95.     } elsif $num < 1.0 {
  96.         while $num < 1.0 {
  97.             $num *= 10;
  98.             $exp--;
  99.         }
  100.     }
  101.     return ($num,$exp);
  102. }
  103. }

  104. class IEEE754::Float {

  105. constant $s-offset = 127;

  106. constant $d-offset = 1023;

  107. has Int            $.sign;        # 符号
  108. has Int            @.exponent;    # 指数
  109. has Int            @.mantissa;    # 尾数
  110. has Precision    $.prec;        # 精度
  111. has Int            @.bin;        # 二进制
  112. method new ($number,:$prec) {
  113.     # 首先将数字的绝对值规格化成 ±d.dd...d × βe,(0 ≤ d i < β) 的小数
  114.     my IEEE754::Normalized $nf = IEEE754::Normalized.new(+($number.abs));
  115.     note 'NormalizedFloat -> ' ~ $nf.perl if $debug;
  116.     my (@m,@e) := self.generateBinary($nf,$prec);
  117.     # 重新整理出二进制数组形式
  118.     my @bin = (@e,@m).flat;
  119.     @bin.unshift: $number > 0 ?? 0 !! 1;
  120.     self.bless( sign => $number > 0 ?? 0 !! 1,exponent => @e,mantissa => @m,prec => $prec,bin    => @bin,);
  121. }
  122. method generateBinary($nf,$prec) {
  123.     my $count = 0;
  124.     my @mantissa;
  125.     # 将尾数匹配出来
  126.     if $nf.mantissa ~~ /1\.@<digits> = (\d)+/ {
  127.         @mantissa.push(+$_) for @<digits>;
  128.     }
  129.     #  转换指数为二进制数组
  130.     my @exponent := ConvertFormat.to-binary-array(
  131.             $nf.exponent + 
  132.             (
  133.                 $prec ~~ Precision::Single ?? $s-offset !! $d-offset
  134.             )
  135.         );
  136.     if $prec ~~ Precision::Single {
  137.         $count = +@mantissa;
  138.         if $count > 23 {
  139.             @mantissa.pop for ^($count - 23);
  140.         } else {
  141.             @mantissa.unshift(0) for ^(23 - $count);
  142.         }
  143.         if +@exponent > 8 {
  144.             die("exponent overflow ...!");
  145.         }
  146.         $count = +@exponent;
  147.         @exponent.unshift(0) for ^(8 - $count);
  148.     } elsif $prec ~~ Precision::Double {
  149.         $count = +@mantissa;
  150.         if $count > 52 {
  151.             @mantissa.pop for ^($count - 52);
  152.         } else {
  153.             @mantissa.unshift(0) for ^(52 - $count);
  154.         }
  155.         if +@exponent > 11 {
  156.             die("exponent overflow ...!");
  157.         }
  158.         $count = +@exponent;
  159.         @exponent.unshift(0) for ^(11 - $count);
  160.     }
  161.     note "mantissa[{+@mantissa}] -> " ~  @mantissa if $debug;
  162.     note "exponent[{+@exponent}] -> " ~  @exponent if $debug;
  163.     return (@mantissa,@exponent);
  164. }
  165. method Str {
  166.     ~@!bin.join;
  167. }
  168. }

  169. 单精度模式

  170. multi sub MAIN(Bool :s(:single($_)),Bool :h($human) = False,Bool :debug($is-debug) = False,*@numbers) {

  171. $debug  = $is-debug;
  172. for @numbers -> $number {
  173.     my IEEE754::Float $float = IEEE754::Float.new($number,prec => Precision::Single);
  174.     say $number ~ '[32:0] ==> ' ~ $float.Str if !$human;
  175.     say $number ~ "[32:0] ==> {$float.sign}:{$float.exponent.join}:{$float.mantissa.join}" if $human;
  176. }
  177. }

  178. 双精度模式

  179. multi sub MAIN(Bool :d(:double($_)),prec => Precision::Double);

  180.     say $number ~ '[64:0] ==> ' ~ $float.Str if !$human;
  181.     say $number ~ "[64:0] ==> {$float.sign}:{$float.exponent.join}:{$float.mantissa.join}" if $human;
  182. }
  183. }

猜你在找的Perl相关文章