Đâ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];
}
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
@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
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