2011-10-05 69 views
3

我讀了關於如何在Perl中將二進制整數轉換爲十進制或反之亦然的堆棧溢出問題How do I convert a binary string to a number in Perl?。但是我怎樣才能做到這一點呢?在Perl中浮點到十進制轉換的二進制表示法

例如,轉換從5.375101.011,反之亦然。

+0

我認爲你正在尋找的'pack'功能。看看'perldoc -f pack'。 – 2011-10-05 23:58:26

+0

@Jack:對,我已經看過這個功能,但仍然無法弄清楚。請幫助一些明確的工作代碼。謝謝。 –

+1

這聽起來像是一個家庭作業問題。 – Ether

回答

3
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; 
} 
+0

很酷,+1。我確實發現了一些小錯誤:'number_to_binary_string'附加'「.0」'爲整數,並且不處理大於2 ** 32-1的數字; 'binary_string_to_number'對沒有小數點或大於2 ** 32-1的數字發出警告。 –

+0

從問題陳述我假設一切都需要一個小數,但你是正確的> = 2 ** 32。用循環替換oct和sprintf作爲練習,讓學生 – ysth

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; 
} 
1

下面是一個有趣的「便攜」實現草圖。它不處理像整數,NaN,infinities,甚至負數的任何有趣的邊緣情況,因爲我很懶,但是擴展它並不會那麼困難。

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

的2 ** 32似乎是一個體繫結構相關的幻數,但實際上它是你點後基本上有多少位精度需要。太小,你得到無害的截斷;太大而且可能會發生溢出(因爲%b可能會在進行格式化之前轉換爲UV)。

+0

您希望'%b。%032b'保留前導零;沒有,.5,.25,.125全部變成「0.1」 – ysth

+0

@ysth好,趕快,謝謝 – hobbs

0
$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");