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];
}
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
@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
Un muy buen artículo sobre esto se puede encontrar aquí: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm – Jenni