2011-01-18 15 views
7

¿Hay alguna forma de obtener la representación binaria de un número de coma flotante en PHP? Algo así como el Double.doubleToRawLongBits() de Java.¿Cómo obtener representación binaria del número de coma flotante en PHP?

Dado un número de coma flotante positivo, me gustaría obtener el mayor número de punto flotante representable que es menor que ese número. En Java, puedo hacerlo así:

double x = Double.longBitsToDouble(Double.doubleToRawLongBits(d) - 1); 

Pero no veo nada similar en PHP.

+0

estoy bastante seguro de que su código Java está libre de errores. ¿Qué sucede cuando solo se establece el bit bajo? Sospecho que ignorar el exponente no funciona ... O cuando pasas del exponente 0 a -1, ¿destruyes los bits de NaN, etc.? – derobert

+1

@derobert: Si puede encontrar una situación en la que mi código Java no funcione, aparte de ± 0, ± Inf o NaN, avíseme (con valores negativos, da el valor más pequeño que el valor dado). Los números positivos de coma flotante IEEE, cuando se convierten en enteros, se ordenan. (Y los valores negativos están ordenados de forma inversa). los exponentes se almacenan como un valor sin signo con un desplazamiento, no complemento a dos, entonces -1 realmente es 1 menos que 0. Si solo se establece el bit bajo, estás viendo el equivalente a Double.MIN_VALUE, y restando uno da 0 , Cuál es la respuesta correcta. – Jenni

+1

Un muy buen artículo sobre esto se puede encontrar aquí: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm – Jenni

Respuesta

2

Ésta no es una respuesta completa, pero la única manera que conozco para poner un flotador en binario es con pack()

+0

parece que esto puede funcionar, pero tendré que jugar con eso ... publicaré una actualización si la hago funcionar. – Jenni

+0

tiene una solución con su sugerencia. No es portátil, y terminé haciendo algo completamente diferente, pero al menos publiqué lo que conseguí trabajando en caso de que beneficiara a alguien más. – Jenni

4

Aquí es una solución que se me ocurrió usar Peter Bailey 's sugerencia. Requiere una versión de 64 bits de PHP. No pretendo que sea de calidad de producción de ninguna manera, pero lo estoy compartiendo en caso de que alguien quiera desarrollarlo. (De hecho terminé haciendo algo diferente por completo después de que envió la pregunta, pero lo dejo aquí como un ejercicio intelectual.)

// Returns the largest double-precision floating-point number which 
// is less than the given value. Only works for positive values. 
// Requires integers to be represented internally with 64 bits (on Linux 
// this usually means you're on 64-bit OS with PHP built for 64-bit OS). 
// Also requires 64-bit double-precision floating point numbers, but I 
// think this is the case pretty much everywhere. 
// Returns false on error. 
function prevDouble($d) { 
    $INT32_MASK = 0xffffffff; 
    if((0x1deadbeef >> 32) !== 1) { 
    echo 'error: only works on 64-bit systems!'; 
    return false; 
    } 
    if($d <= 0) { 
    return false; 
    } 
    $beTest = bin2hex(pack('d', 1.0)); //test for big-endian 
    if(strlen($beTest) != 16) { 
    echo 'error: system does not use 8 bytes for double precision!'; 
    return false; 
    } 

    if($beTest == '3ff0000000000000') { 
    $isBE = true; 
    } 
    else if($beTest == '000000000000f03f') { 
    $isBE = false; 
    } 
    else { 
    echo 'error: could not determine endian mode!'; 
    return false; 
    } 

    $bin = pack('d', $d); 

    //convert to 64-bit int 
    $int = 0; 
    for($i = 0; $i < 8; $i++) 
    $int = ($int << 8) | ord($bin[$isBE ? $i : 7 - $i]); 

    $int--; 
    //convert back to double 
    if($isBE) 
    $out = unpack('d', pack('N', ($int >> 32) & $INT32_MASK) . pack('N', $int & $INT32_MASK)); 
    else 
    $out = unpack('d', pack('V', $int & $INT32_MASK) . pack('V', ($int >> 32) & $INT32_MASK)); 

    return $out[1]; 
} 
3

Como respuesta adicional que no toda la cuestión, sino al título:

Si desea ver cómo sus flotadores se ve como un binario:

function floatToBinStr($value) { 
    $bin = ''; 
    $packed = pack('d', $value); // use 'f' for 32 bit 
    foreach(str_split(strrev($packed)) as $char) { 
     $bin .= str_pad(decbin(ord($char)), 8, 0, STR_PAD_LEFT); 
    } 
    return $bin; 
} 

echo floatToBinStr(0.0000000000000000000000000000000000025).PHP_EOL; 
echo floatToBinStr(0.25).PHP_EOL; 
echo floatToBinStr(0.5).PHP_EOL; 
echo floatToBinStr(-0.5).PHP_EOL; 

salida:

0011100010001010100101011010010110110111111110000111101000001111 
0011111111010000000000000000000000000000000000000000000000000000 
0011111111100000000000000000000000000000000000000000000000000000 
1011111111100000000000000000000000000000000000000000000000000000 
+0

Disculpe, ¿por qué hizo strrev ($ packed) antes de dividirlo? – tonix

+1

@ user3019105 Uh, hace mucho tiempo ... Creo que los bytes (¡no los bits!) Se almacenan en la dirección opuesta a la que normalmente se muestra en los ejemplos. p.ej. http://www.binaryconvert.com/result_float.html?decimal=048046050053 – flori

+0

Así que si hago 'pack (" d ", 0.25)' si tengo flotadores de 32 bits obtendré esta cadena binaria: '00000000 00000000 00000001 01111100' en lugar de este: '00111110 10000000 00000000 00000000' ??? ¿Derecha?¿Por qué pack() hace esto? – tonix

0

java:

long lnBits = Double.doubleToLongBits(longValue); 
Byte[] bits = new byte [] { 
    (byte) ((value << 56) >>> 56), 
    (byte) ((value << 48) >>> 56), 
    (byte) ((value << 40) >>> 56), 
    (byte) ((value << 32) >>> 56), 
    (byte) ((value << 24) >>> 56), 
    (byte) ((value << 16) >>> 56), 
    (byte) ((value << 8) >>> 56), 
    (byte) (value >>> 56)} 

php:

$bits = $bitsFromJava; 
$str=""; 
for($i=0;$i<8;i++){ 
    $str.=chr($bits[$i]); 
} 
$longValue=unpack('d',$str); 

$bitsToJava=array(); 
for(str_split(pack($longValue)) as $chr){ 
    $bitsToJava[]=ord($chr); 
} 
Cuestiones relacionadas