【问题标题】:How to get binary representation of floating-point number in PHP?如何在 PHP 中获取浮点数的二进制表示?
【发布时间】:2011-01-18 21:59:24
【问题描述】:

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

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

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

但我在 PHP 中没有看到类似的东西。

【问题讨论】:

  • 我很确定你的 Java 代码有问题。当只设置低位时会发生什么?我怀疑忽略指数不起作用......或者当你从指数 0 到 -1 时,你会破坏 NaN 等位吗?
  • @derobert:如果您发现我的 Java 代码无法正常工作,除了 ±0、±Inf 或 NaN,请告诉我(负值会使最小值更大比给定值)。正 IEEE 浮点数在转换为整数时会进行排序。 (并且负值是反向排序的)。指数存储为带有偏移量的无符号值,而不是二进制补码,因此 -1 确实比 0 小 1。如果仅设置了低位,您将看到 Double.MIN_VALUE 的等价物,减一得到 0 ,这是正确的答案。
  • 一篇非常好的文章可以在这里找到:cygnus-software.com/papers/comparingfloats/comparingfloats.htm
  • 感谢您的解释和教育,这听起来确实有效。此外,您应该将代码放入答案中。回答你自己的问题没有错。
  • @derobert - 好的,我将我的解决方案移到了答案中

标签: php floating-point numerical-methods


【解决方案1】:

这是我使用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];
}

【讨论】:

    【解决方案2】:

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

    如果你想看看你的浮点数看起来像一个二进制文件:

    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
    

    【讨论】:

    • 对不起,你为什么在拆分之前做了strrev($packed)?
    • @user3019105 呃,很久以前……我认为字节(不是位!)的存储方向与示例中通常显示的方向相反。例如binaryconvert.com/result_float.html?decimal=048046050053
    • 所以如果我做 pack("d", 0.25) 如果我有 32 位浮点数,我会得到这个二进制字符串:00000000 00000000 00000001 01111100 而不是这个:00111110 10000000 00000000 00000000???对?为什么 pack() 会这样做?
    • 也许这就是 PHP 中浮点数的内部表示? (也许它甚至与机器相关,因为“d”的 pack() 状态的文档?)注意,您正在镜像示例中的整行位,但仅镜像块/字节,块中的位/字节不是。
    • 像这样,01111100 00000001 00000000 00000000 对吧?
    【解决方案3】:

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

    【讨论】:

    • 看起来这可能有效,但我必须尝试一下...如果我能正常工作,我会发布更新。
    • 根据您的建议得到了解决方案。它不是便携式的,我最终做了一些完全不同的事情,但我至少发布了我正在工作的内容,以防其他人受益。
    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-06-16
      • 2016-05-01
      • 2018-04-10
      • 1970-01-01
      • 2018-10-11
      • 2016-01-01
      • 1970-01-01
      相关资源
      最近更新 更多