2011-01-18 12 views
7

C'è un modo per ottenere la rappresentazione binaria di un numero in virgola mobile in PHP? Qualcosa come Java Double.doubleToRawLongBits().Come ottenere la rappresentazione binaria del numero in virgola mobile in PHP?

Dato un numero a virgola mobile positivo, mi piacerebbe ottenere il più grande numero a virgola mobile rappresentabile che sia inferiore a quel numero. In Java, posso farlo in questo modo:

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

Ma non vedo nulla di simile in PHP.

+0

sono abbastanza sicuro che il codice Java è bacato. Cosa succede quando viene impostato solo il bit basso? Sospetto che ignorare l'esponente non funzioni ... O quando passi dall'esponente 0 a -1, distruggi i bit di NaN ecc.? – derobert

+1

@derobert: Se riesci a trovare una situazione in cui il mio codice Java non funziona, a parte ± 0, ± Inf o NaN, fammi sapere (con valori negativi dà il valore più piccolo maggiore del valore dato). I numeri a virgola mobile IEEE positivi, quando convertiti in numero intero, vengono ordinati. (E i valori negativi sono ordinati in ordine inverso). gli esponenti sono memorizzati come un valore senza segno con un offset, non un complemento di due, quindi -1 è veramente 1 in meno di 0. Se è impostato solo il bit basso, stai osservando l'equivalente di Double.MIN_VALUE, e sottraendo uno dà 0 , qual è la risposta corretta. – Jenni

+1

Un ottimo articolo su questo può essere trovato qui: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm – Jenni

risposta

2

Questa non è una risposta completa, ma l'unico modo che conosco per mettere un galleggiante in binario è con pack()

+0

sembra che questo possa funzionare, ma dovrò giocarci ... pubblicherò un aggiornamento se lo faccio funzionare. – Jenni

+0

ha trovato una soluzione con il tuo suggerimento. Non è portabile, e ho finito per fare qualcosa di completamente diverso, ma ho almeno pubblicato quello che ho lavorato nel caso in cui avrebbe giovato a chiunque altro. – Jenni

4

Ecco una soluzione mi è venuta con l'utilizzo di Peter Bailey 's suggerimento. Richiede la versione a 64 bit di PHP. Non ritengo che ciò sia in alcun modo la qualità della produzione, ma lo condivido nel caso in cui qualcuno volesse costruire su di esso. (In realtà ho finito per fare qualcosa di completamente diverso dopo che ho postato la domanda, ma lo lascio qui come un esercizio intellettuale.)

// 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

Come risposta supplementare non di tutta la questione, ma per il titolo:

Se volete vedere come i vostri carri appare come 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; 

uscita:

0011100010001010100101011010010110110111111110000111101000001111 
0011111111010000000000000000000000000000000000000000000000000000 
0011111111100000000000000000000000000000000000000000000000000000 
1011111111100000000000000000000000000000000000000000000000000000 
+0

Scusa, perché hai eseguito strrev ($ confezionato) prima di dividerlo? – tonix

+1

@ user3019105 Uh, molto tempo fa ... Penso che i byte (non i bit!) Siano memorizzati nella direzione opposta rispetto a quella normalmente mostrata negli esempi. per esempio. http://www.binaryconvert.com/result_float.html?decimal=048046050053 – flori

+0

Quindi se faccio 'pack (" d ", 0.25)' se ho 32 bit float ottengo otterrà questa stringa binaria: '00000000 00000000 00000001 01111100' invece di questo: '00111110 10000000 00000000 00000000' ??? Destra?Perché pack() fa questo? – 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); 
} 
Problemi correlati