Pregunta

Tengo un proyecto en el que una función recibe cuatro caracteres de 8 bits y necesita para convertir el 32 bits IEEE-754 float resultante a un número regular de Perl. Parece que debe haber una manera más rápida que el código de trabajo a continuación, pero no he sido capaz de averiguar una función paquete más simple que funciona.

No funciona, pero parece que está cerca:

$float = unpack("f", pack("C4", @array[0..3]);  # Fails for small numbers

Obras:

@bits0 = split('', unpack("B8", pack("C", shift)));
@bits1 = split('', unpack("B8", pack("C", shift)));
@bits2 = split('', unpack("B8", pack("C", shift)));
@bits3 = split('', unpack("B8", pack("C", shift)));
push @bits, @bits3, @bits2, @bits1, @bits0;

$mantbit = shift(@bits);
$mantsign = $mantbit ? -1 : 1;
$exp = ord(pack("B8", join("",@bits[0..7])));
splice(@bits, 0, 8);

# Convert fractional float to decimal
for (my $i = 0; $i < 23; $i++) {
    $f = $bits[$i] * 2 ** (-1 * ($i + 1));
    $mant += $f;
}
$float = $mantsign * (1 + $mant) * (2 ** ($exp - 127));

Alguien tiene una mejor manera?

¿Fue útil?

Solución

Me gustaría tener el enfoque opuesto: se olvide de desembalaje, se adhieren a cambio de bit

.

En primer lugar, montar su palabra de 32 bits. Dependiendo de orden de bits, esto podría tener que ser al revés:

my $word = ($byte0 << 24) + ($byte1 << 16) + ($byte2 << 8) + $byte3;

Ahora extraer las partes de la palabra: el bit de signo, exponente y mantisa:

my $sign = ($word & 0x80000000) ? -1 : 1;
my $expo = (($word & 0x7F800000) >> 23) - 127;
my $mant = ($word & 0x007FFFFF | 0x00800000);

Montar el flotador:

my $num = $sign * (2 ** $expo) * ( $mant / (1 << 23));

Hay algunos ejemplos en Wikipedia .

  • probado esto en 0xC2ED4000 => -118.625 y funciona.
  • probado esto en 0x3E200000 => 0,15625 y encontró un error! (Fijo)
  • No se olvide de manejar infinitos y cuando NaNs $ Expo == 255

Otros consejos

La mejor manera de hacer esto es usar pack () .

my @bytes = ( 0xC2, 0xED, 0x40, 0x00 );
my $float = unpack 'f', pack 'C4', @bytes;

O si el origen y el destino tienen diferentes endianness:

my $float = unpack 'f', pack 'C4', reverse @bytes;

Usted dice que este método "no funciona - que parece que está cerca" y "falla por números pequeños", pero usted no da un ejemplo. Supongo que lo que en realidad se está viendo es el redondeo donde, por ejemplo, un número está lleno como 1.234, pero es desembalado como 1.23399996757507. Eso no es una función del pack (), sino de la precisión de un flotador de 4 bytes.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top