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];
}
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
@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
Un ottimo articolo su questo può essere trovato qui: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm – Jenni