我讀了關於如何在Perl中將二進制整數轉換爲十進制或反之亦然的堆棧溢出問題How do I convert a binary string to a number in Perl?。但是我怎樣才能做到這一點呢?在Perl中浮點到十進制轉換的二進制表示法
例如,轉換從5.375
到101.011
,反之亦然。
我讀了關於如何在Perl中將二進制整數轉換爲十進制或反之亦然的堆棧溢出問題How do I convert a binary string to a number in Perl?。但是我怎樣才能做到這一點呢?在Perl中浮點到十進制轉換的二進制表示法
例如,轉換從5.375
到101.011
,反之亦然。
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」'爲整數,並且不處理大於2 ** 32-1的數字; 'binary_string_to_number'對沒有小數點或大於2 ** 32-1的數字發出警告。 –
從問題陳述我假設一切都需要一個小數,但你是正確的> = 2 ** 32。用循環替換oct和sprintf作爲練習,讓學生 – ysth
下面是一個機器和構建特定的實現(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;
}
下面是一個有趣的「便攜」實現草圖。它不處理像整數,NaN,infinities,甚至負數的任何有趣的邊緣情況,因爲我很懶,但是擴展它並不會那麼困難。
(my $bin = sprintf "%b.%032b", int($num), 2**32 * ($num - int($num)))
=~ s/\.?0+$//;
的2 ** 32似乎是一個體繫結構相關的幻數,但實際上它是你點後基本上有多少位精度需要。太小,你得到無害的截斷;太大而且可能會發生溢出(因爲%b
可能會在進行格式化之前轉換爲UV)。
$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");
我認爲你正在尋找的'pack'功能。看看'perldoc -f pack'。 – 2011-10-05 23:58:26
@Jack:對,我已經看過這個功能,但仍然無法弄清楚。請幫助一些明確的工作代碼。謝謝。 –
這聽起來像是一個家庭作業問題。 – Ether