【问题标题】:Binary representation of float to decimal conversions in PerlPerl 中浮点到十进制转换的二进制表示
【发布时间】:2014-03-19 01:55:03
【问题描述】:

我阅读了堆栈溢出问题How do I convert a binary string to a number in Perl?,了解如何在 Perl 中将二进制整数转换为十进制,反之亦然。但是我该如何为 float 执行此操作?

例如,从5.375 转换为101.011,反之亦然。

【问题讨论】:

  • 我认为您正在寻找pack 函数。看看perldoc -f pack
  • @Jack:对,我看过那个函数,但还是想不通。请帮助提供一些明确的工作代码。谢谢。
  • 这听起来像是一道作业题。

标签: perl


【解决方案1】:
sub number_to_binary_string {
    my $in = shift;
    my $sign = $in < 0 and $in = abs $in;
    my $out = sprintf "%b.", int $in;
    substr $out, 0, 0, '-' if $sign;
    $in -= int $in;
    do {
        if ($in >= .5) {
            $out .= '1';
            $in -= .5;
        }
        else {
            $out .= '0';
        }
        $in *= 2;
    } while $in > 0;
    return $out;
}

sub binary_string_to_number {
    my $in = shift;
    my ($int,$frac) = split /\./, $in;
    my $sign = $int =~ s/^-//;
    my $out = oct "0b$int";
    my $mult = 1;
    for my $digit (split //, $frac) {
        $mult *= .5;
        $out += $mult * $digit;
    }
    $out = -$out if $sign;
    return $out;
}

【讨论】:

  • 酷,+1。我确实发现了一些小错误:number_to_binary_string".0" 附加到整数并且不处理大于 232-1; 的数字; binary_string_to_number 对缺少小数点或大于 232-1 的数字发出警告。
  • 从问题陈述中我假设一切都需要/需要小数,但你是对的 >=2**32。用循环替换 oct 和 sprintf 留给学生练习
【解决方案2】:

以下是特定于机器和构建的实现(NV = little-endian double)。

它返回存储的数字准确,它支持 NaN、Infinity、-Infinity 和 -0 和次正规。它修剪前导零和尾随小数零。

sub double_to_bin {
   my ($n) = @_;
   my ($s, $e, $m) = unpack 'a a11 a52', unpack 'B64', "".reverse pack 'F', $n;
   $s = $s ? '-' : '';
   $e = oct("0b$e");

   if ($e == 0x7ff) {
      return ($m =~ /1/) ? 'NaN' : $s . 'Infinity'
   } elsif ($e == 0x000) {
      $m = "0$m";  $e -= 52;
   } else {
      $m = "1$m";  $e -= 1075;
   }

   if ($e >= 0) {
      $m .= ('0' x $e);
   } elsif ($e >= -52) {
      substr($m, $e+53, 0, '.');
   } else {
      $m = '0.' . ('0' x (-$e-53)) . $m;
   }

   $m =~ s/^0+(?!\.)//;
   $m =~ s/(?:\..*1\K|\.)0+\z//;
   return $s . $m;
}

【讨论】:

    【解决方案3】:

    这是一个有趣的“便携式”实现的草图。它不处理任何有趣的边缘情况,如整数、NaN、无穷大,甚至是负数,因为我很懒,但扩展它不会那么难。

    (my $bin = sprintf "%b.%032b", int($num), 2**32 * ($num - int($num)))
        =~ s/\.?0+$//;
    

    2**32 看起来像是一个特定于架构的幻数,但实际上它基本上只是在点之后您想要多少位精度。太小,你会得到无害的截断;太大并且有可能溢出(因为%b 可能会在格式化之前的某个时间转换为 UV)。

    【讨论】:

    • 您希望%b.%032b 保留前导零;没有,.5, .25, .125 都变成“0.1”
    【解决方案4】:
    $TO_BIN = '-b';
    $TO_DEC = '-d';
    ($op, $n ) = @ARGV;
    die("USAGE: $0 -b <dec_to_convert> | -d <bin_to_convert>\n") unless ( $op =~ /^($TO_BIN|$TO_DEC)$/ && $n );
    
    for (split(//,$n)) {
        if ($_ eq ".") {
            $f=".";
        } else {
            if (defined $f) { $f.=$_ } else { $i.=$_ }
        }   
    } 
    $ci = sprintf("%b", $i)          if $op eq $TO_BIN;
    $ci = sprintf("%d", eval "0b$i") if $op eq $TO_DEC;
    
    @f=split(//,$f) if $f;
    if ($op eq $TO_BIN) {
        while( $f && length($cf) < 16 ) { 
            ($f *= 2) =~ s/(\d)(\.?.*)/$2/;
            $cf .= $1 ? '1' : '0';
        }
    } else {
        for ($i=1;$i<@f;$i++) {
            $cf = ($cf + $f[@f-$i])/2;
        }
    }
    $cf=~s/^.*\.|^/./ if $cf;
    print("$ci$cf\n");
    

    【讨论】:

      猜你喜欢
      • 2021-09-10
      • 2015-10-11
      • 2017-02-23
      • 2014-11-24
      • 1970-01-01
      • 2016-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多