Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get binary representation of floating-point number in PHP?

Is there any way to get the binary representation of a floating point number in PHP? Something like Java's Double.doubleToRawLongBits().

Given a positive floating point number, I'd like to get the largest representable floating-point number which is less than that number. In Java, I can do it like this:

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

But I'm not seeing anything similar in PHP.

like image 323
Jenni Avatar asked Jan 18 '11 21:01

Jenni


1 Answers

Here is a solution I came up with using Peter Bailey's suggestion. It requires 64-bit version of PHP. I don't claim this to be production-quality in any way, but I'm sharing in case anyone wants to build upon it. (In fact I ended up doing something different entirely after I posted the question, but I leave it here as an intellectual exercise.)

// 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];
}
like image 142
Jenni Avatar answered Sep 27 '22 17:09

Jenni