如何在 PHP 中获取浮点数的二进制表示?

发布于 2024-10-12 09:02:49 字数 380 浏览 6 评论 0原文

有没有办法在 PHP 中获取浮点数的二进制表示形式?类似于 Java 的 Double.doubleToRawLongBits()

给定一个正浮点数,我想获得小于该数字的最大可表示浮点数。在 Java 中,我可以这样做:

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

但我在 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

慕烟庭风 2024-10-19 09:02:49

这是我使用 Peter Bailey 的建议提出的解决方案。它需要 64 位版本的 PHP。我不以任何方式声称这是生产质量的,但我分享以防有人想要在它的基础上进行构建。 (事实上​​,在发布问题后,我最终做了一些完全不同的事情,但我把它留在这里作为智力练习。)

// 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];
}

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];
}
Oo萌小芽oO 2024-10-19 09:02:49

作为不是整个问题而是标题的附加答案:

如果您想查看浮点数作为二进制文件的外观:

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;

输出:

0011100010001010100101011010010110110111111110000111101000001111
0011111111010000000000000000000000000000000000000000000000000000
0011111111100000000000000000000000000000000000000000000000000000
1011111111100000000000000000000000000000000000000000000000000000

As an additional answer not to the whole question but to the title:

If you want to see how your floats looks as a binary:

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;

Output:

0011100010001010100101011010010110110111111110000111101000001111
0011111111010000000000000000000000000000000000000000000000000000
0011111111100000000000000000000000000000000000000000000000000000
1011111111100000000000000000000000000000000000000000000000000000
一萌ing 2024-10-19 09:02:49

这不是完整的答案,但我知道将浮点数放入二进制文件的唯一方法是使用 pack()

This isn't a full answer, but the only way I know of to put a float into binary is with pack()

深空失忆 2024-10-19 09:02:49

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);
}

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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文