2009-04-20 12 views
10

Tengo un proyecto en el que una función recibe cuatro caracteres de 8 bits y necesita convertir el flotador IEEE-754 resultante de 32 bits en un número Perl normal. Parece que debería haber una manera más rápida que el código de trabajo a continuación, pero no he podido encontrar una función de paquete más simple que funcione.¿Cómo puedo convertir cuatro caracteres en un flotador IEEE-754 de 32 bits en Perl?

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?

+1

Me intriga que su fragmento superior "no funcione pero esté cerca": ¿puede identificar las diferencias? P.ej. tomando el resultado de unpack() y convirtiéndolo nuevamente a los 4 bytes, y luego buscando bits que sean diferentes entre la entrada y la salida final? –

Respuesta

13

Tomaría el enfoque opuesto: olvídate de desempacar, adhiérete a los giros.

Primero, ensamble 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 su 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 encontrado un error! (Fijo)
  • No se olvide de manejar infinitos y cuando NaNs $ == 255
+0

Muy agradable. Esto funciona y es dos veces más rápido para mi caso de prueba y las respuestas son correctas (probadas con 100 mil números únicos). En el futuro tendré que intentar ensuciarme las manos e intentar hacer algunas operaciones de poco. ¡Gracias! –

+0

No se preocupe, disfrute. Debería ser bastante rápido. PD: "cuando $ expo == 255" debería leer "cuando $ expo == 128" ... Olvidé el desplazamiento. – NickZoic

+0

my $ word = unpack ("N", $ bytes); debería ser mucho más rápido –

5

La mejor manera de hacer esto es utilizar Expo 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 no das un ejemplo. Supongo que lo que estás viendo en realidad está redondeando donde, por ejemplo, un número está empaquetado como 1.234, pero está desempaquetado como 1.23399996757507. Esa no es una función de pack(), sino de la precisión de un flotante de 4 bytes.

+1

bien podría estar en lo cierto. Por otro lado, mi código produciría exactamente los mismos errores, creo. pack/unpack 'f' realiza conversiones a/desde "Un flotador de precisión simple en el formato nativo". ... tal vez sea lo que sea que Dan esté corriendo no es * bastante * IEEE-754? – NickZoic

+1

"Mi código produciría exactamente los mismos errores". Su código * no * produce los mismos resultados que el método pack(), pero no es un error, simplemente se redondea. Por ejemplo, intente desembalar 0x3F9DF3B6, que está empaquetado como un flotador 1.234, usando su método y el método del paquete. Además, el formato flotante "nativo" de la gran mayoría de los sistemas en los que se ejecuta Perl es IEEE 754. Si el OP estuviera en un sistema con un formato flotante diferente, sería lo suficientemente inusual como para saberlo. – jmcnamara

+0

El redondeo es un problema para algo como e-39 –

Cuestiones relacionadas