2011-01-18 11 views
7

Có cách nào để có được biểu diễn nhị phân của một số dấu chấm động trong PHP không? Một cái gì đó giống như của Java Double.doubleToRawLongBits().Làm thế nào để có được biểu diễn nhị phân của số dấu chấm động trong PHP?

Với số dấu phẩy động dương, tôi muốn có số dấu phẩy động lớn nhất nhỏ hơn số đó. Trong Java, tôi có thể làm như sau:

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

Nhưng tôi không thấy bất kỳ điều gì tương tự trong PHP.

+0

Tôi khá chắc chắn mã Java của bạn là lỗi. Điều gì sẽ xảy ra khi chỉ có bit thấp được thiết lập? Tôi nghi ngờ bỏ qua số mũ không hoạt động ... Hoặc khi bạn đi từ số mũ 0 đến -1, bạn có phá hủy các bit NaN vv không? – derobert

+1

@derobert: Nếu bạn có thể tìm thấy trường hợp mã Java của tôi không hoạt động, ngoài ± 0, ± Inf hoặc NaN, hãy cho tôi biết (với giá trị âm, giá trị nhỏ nhất lớn hơn giá trị đã cho). Số điểm dấu chấm động IEEE tích cực, khi được chuyển đổi thành số nguyên, được sắp xếp. (Và các giá trị âm được sắp xếp ngược lại). số mũ được lưu trữ dưới dạng giá trị chưa ký với giá trị bù trừ, không bổ sung twos, vì vậy -1 thực sự là 1 nhỏ hơn 0. Nếu chỉ bit thấp được đặt, bạn đang xem tương đương với Double.MIN_VALUE và trừ một giá trị 0 , Cái nào là câu trả lời đúng. – Jenni

+1

Một bài viết rất tốt về điều này có thể được tìm thấy ở đây: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm – Jenni

Trả lời

2

Đây không phải là một câu trả lời đầy đủ, nhưng cách duy nhất tôi biết để đặt một phao vào nhị phân là với pack()

+0

trông như thế này có thể hoạt động, nhưng tôi sẽ phải chơi với nó ... tôi sẽ đăng cập nhật nếu tôi làm việc đó. – Jenni

+0

có giải pháp với đề xuất của bạn. Nó không phải là di động, và tôi đã hoàn thành một cái gì đó hoàn toàn khác, nhưng tôi ít nhất đăng những gì tôi đã làm việc trong trường hợp nó sẽ có lợi cho bất cứ ai khác. – Jenni

4

Đây là một giải pháp tôi đã đưa ra với việc sử dụng Peter Bailey 's đề nghị. Nó đòi hỏi phiên bản PHP 64 bit. Tôi không cho rằng đây là chất lượng sản xuất theo bất kỳ cách nào, nhưng tôi đang chia sẻ trong trường hợp bất cứ ai muốn xây dựng nó. (Trong thực tế tôi đã kết thúc làm một cái gì đó hoàn toàn khác sau khi tôi đăng các câu hỏi, nhưng tôi để nó ở đây như là một bài tập trí tuệ.)

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

Như một câu trả lời bổ sung không phải toàn bộ câu hỏi nhưng với tiêu đề:

Nếu bạn muốn xem cách nổi của bạn trông như một nhị phân:

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 
+0

Xin lỗi, tại sao bạn làm strrev (đóng gói $) trước khi chia nhỏ? – tonix

+1

@ user3019105 Uh, lâu rồi ... Tôi nghĩ rằng các byte (không phải bit!) Được lưu trữ theo hướng ngược lại so với bình thường trong ví dụ. ví dụ. http://www.binaryconvert.com/result_float.html?decimal=048046050053 – flori

+0

Vì vậy, nếu tôi làm 'gói (" d ", 0,25)' nếu tôi có 32 bit nổi tôi nhận được sẽ nhận được chuỗi nhị phân này: '00000000 00000000 00000001 01111100' thay vì cái này: '00111110 10000000 00000000 00000000' ??? Đúng?Tại sao pack() làm điều này? – tonix

0

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