Có cách nào chuẩn để lấy giá trị dấu phẩy động tiếp theo thứ n trong C ++ không


8

C ++ có std::nextafter(), trả về giá trị đại diện tiếp theo sau một giá trị dấu phẩy động đã cho f . Trong trường hợp của tôi, tôi muốn cho phép n bit slop trong các bit mantissa thấp hơn, vì vậy 3 bit slop sẽ yêu cầu nhận giá trị tiếp theo thứ 8 sau một số giá trị f đã cho . Tôi có thể gọi nextafter()tám lần, nhưng có cách nào tốt hơn để xử lý việc này không?

Đối với hầu hết các giá trị, bạn có thể nhận được bằng cách truyền giá trị FP vào uint_64, thêm dung sai ( 1<<3cho 3 bit của độ dốc), sau đó chuyển trở lại double, nhờ vào cách bố trí của IEEE 754. Tuy nhiên, điều đó phụ thuộc vào điểm nổi của IEEE 754 ( một giả định tốt, nhưng cũng không phải là đá rắn).

(Đối với nền, tôi muốn sử dụng điều này để nâng các điểm giao nhau trên bề mặt tia, đôi khi nằm bên trong bề mặt do không chính xác của FP. Những người quen thuộc với điểm nổi mạnh mẽ sẽ hiểu tại sao lại epsilonlà một giải pháp khủng khiếp.)


Có vẻ như bạn không cần chính xác giá trị tiếp theo thứ 8. Nhân số f (giả sử là dương) với 1,00 ... 001 có đủ tốt không?
Marc Glisse

Bạn cũng có thể sử dụng giá trị của std::numeric_limits<T>::is_iec559để kiểm tra xem IEEE 754 có được sử dụng hay không và chuyên môn hóa chức năng cho phù hợp.
IlCapitano

Câu trả lời:


2

Một cách tiếp cận ngây thơ có thể nhân 8 lần khoảng cách giữa một giá trị và số float có thể biểu diễn tiếp theo, thay vì gọi 8 lần std::nextafter

double advance_float(double x, int d)
{
    double step = std::copysign((std::nextafter(x, x + d) - x) * d, d);
    return x + step;
}

Dưới đây là một số thử nghiệm, nhưng tùy thuộc vào bạn để xác định xem liệu điều này có phù hợp với trường hợp sử dụng của bạn hay không.

Biên tập

Theo ghi nhận của Steve Hollash , xcó thể lớn đến mức đó x + d == d. Daniel Jour đề nghị tận dụng frexp(và ldexp), nhưng trong lần thử sau, tôi sẽ sử dụng một cách tiếp cận khác để xác định hướng.

double advance_float(double x, int d)
{
    const double to = std::copysign(std::numeric_limits<double>::infinity(), d);
    const double next = std::nextafter(x, to);
    return x + std::copysign(d * (next - x), d);
}

Lưu ý rằng nó giả định rằng std::numeric_limits<double>::has_infinity == true, nếu không ::lowest()::max()phải được sử dụng.

Đó là một số kết quả

         xd trước x tiếp theo
-------------------------------------------------- ----------------------------------------
           1 1 0x1.fffffffffffffp-1 0x1p + 0 0x1.0000000000001p + 0
           1 8 0x1.ffffffffffff8p-1 0x1p + 0 0x1.0000000000008p + 0
     3.14159 8 0x1.921fb54442d1p + 1 0x1.921fb54442d18p + 1 0x1.921fb54442d2p + 1
      100,01 8 0x1.900a3d70a3d69p + 6 0x1.900a3d70a3d71p + 6 0x1.900a3d70a3d79p + 6
     -100,01 8 -0x1.900a3d70a3d79p + 6 -0x1.900a3d70a3d71p + 6 -0x1.900a3d70a3d69p + 6
       1e + 67 8 0x1.7bd29d1c87a11p + 222 0x1.7bd29d1c87a19p + 222 0x1.7bd29d1c87a21p + 222
       1e-59 8 0x1.011c2eaabe7dp-196 0x1.011c2eaabe7d8p-196 0x1.011c2eaabe7ep-196
           0 8 -0x0.0000000000008p-1022 0x0p + 0 0x0.0000000000008p-1022
4.94066e-324 8 -0x0.0000000000007p-1022 0x0.0000000000001p-1022 0x0.0000000000009p-1022

Cách tiếp cận thú vị. Như đã viết x+d, tuy nhiên không phải là thứ bạn đang tìm kiếm. Nếu x lớn thì (x + d) == x. Nhưng tôi thích ý tưởng: tính giá trị bằng bit thứ tự thấp nhất với số mũ phù hợp, được chia tỷ lệ theo "độ dốc", sau đó thêm vào giá trị ban đầu.
Steve Hollasch

@SteveHollasch Đúng, đó là vấn đề với nextafter, làm thế nào để vượt qua "hướng".
Bob__

Thêm frexptrước và ldexpsau này nên làm việc, không?
Daniel Jour

@DanielJour Đỗ bạn có ý nghĩa này ? Nó dường như "làm việc", nhưng tôi chắc chắn thiếu rất nhiều trường hợp góc.
Bob__

Kỳ diệu. Không chỉ là một giải pháp, mà là một loạt các công cụ mới mà tôi không biết. Với frexpđặc biệt, tôi có thể di chuyển thế giới. Cảm ơn bạn!
Steve Hollasch

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.