Двоичное представление числа с плавающей запятой в Perl - PullRequest
3 голосов
/ 06 октября 2011

Я прочитал вопрос переполнения стека Как преобразовать двоичную строку в число в Perl? о том, как преобразовать двоичные числа в десятичное или наоборот в Perl. Но как мне сделать это и для float?

Например, преобразование из 5.375 в 101.011 и наоборот.

Ответы [ 4 ]

3 голосов
/ 06 октября 2011
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;
}
2 голосов
/ 06 октября 2011

Ниже приведена реализация, специфичная для машины и сборки (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;
}
1 голос
/ 06 октября 2011

Вот эскиз интересной «портативной» реализации.Он не обрабатывает ни одного из интересных краевых случаев, таких как целые числа, NaN, бесконечности или даже отрицательные числа, потому что я ленив, но расширить его не будет так сложно.

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

** 32 похоже на магическое число для конкретной архитектуры, но на самом деле это просто то, сколько бит точности вы хотите после точки.Слишком маленький, и вы получите безобидное усечение;слишком велик и может привести к переполнению (поскольку %b, вероятно, когда-нибудь преобразуется в UV перед выполнением его форматирования).

0 голосов
/ 07 октября 2011
$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");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...