簡體   English   中英

將范圍分成均勻間隔

[英]Split range into uniform intervals

我想將具有double邊框的范圍分割為N>=2等於或接近相等的間隔。

我在GNU Scientific Library中找到了一個合適的函數:

make_uniform (double range[], size_t n, double xmin, double xmax)
{
  size_t i;

  for (i = 0; i <= n; i++)
    {
      double f1 = ((double) (n-i) / (double) n);
      double f2 = ((double) i / (double) n);
      range[i] = f1 * xmin +  f2 * xmax;
    }
}

但是,什么時候
xmin = 241141 (二進制0x410D6FA800000000
xmax = 241141.0000000001 (二進制0x410D6FA800000003
N = 3
功能產生

[0x410D6FA800000000,
 0x410D6FA800000000,
 0x410D6FA800000002,
 0x410D6FA800000003]

而不是期望

[0x410D6FA800000000,
 0x410D6FA800000001,
 0x410D6FA800000002,
 0x410D6FA800000003]

如何在不使用長算術的情況下實現均勻性(我已經有一個很長的算術解決方案,但它很丑陋而且很慢)? bit twiddling和x86(x86-64,因此沒有擴展精度)匯編程序例程是可以接受的。

更新:

需要一般的解決方案,沒有前提是xminxmax具有相等的指數和符號:

  • xminxmax可以是除無窮大和NaN之外的任何值(為簡單起見,可能還排除非規范化值)。
  • xmin < xmax
  • (1<<11)-1>=N>=2
  • 我准備好主要(2-3個訂單)的性能損失

我看到兩個選擇:將操作重新排序為xmin + (i * (xmax - xmin)) / n ,或直接處理二進制表示。 這是兩個例子。

#include <iostream>
#include <iomanip>

int main() {
    double xmin = 241141;
    double xmax = 241141.0000000001;
    size_t n = 3, i;
    double range[4];

    std::cout << std::setprecision(std::numeric_limits<double>::digits10) << std::fixed;

    for (i = 0; i <= n; i++) {
        range[i] = xmin + (i * (xmax - xmin)) / n;

        std::cout << range[i] << "\n";
    }
    std::cout << "\n";

    auto uxmin = reinterpret_cast<unsigned long long&>(xmin);
    auto uxmax = reinterpret_cast<unsigned long long&>(xmax);

    for (i = 0; i <= n; i++) {
        auto rangei = ((n-i) * uxmin + i * uxmax) / n;
        range[i] = reinterpret_cast<double&>(rangei);

        std::cout << range[i] << "\n";
    }
}

住在Coliru

x87仍然存在於x86-64中,主流操作系統的64位內核可以正確保存/恢復64位進程的x87狀態。 盡管您可能已閱讀過,但x87完全可用於64位代碼。

在Windows之外(即x86-64 System V ABI在其他地方使用), long double是80位原生x87原生格式。 如果您不關心ARM / PowerPC的可移植性/在HW中只有64位精度的其他任何東西,這可能只解決x86 / x86-64的精度問題。

可能最好只使用long double的功能內的臨時工具。

我不確定你要在Windows上做什么來讓編譯器發出80位擴展FP數學。 它在asm中肯定是可能的,並且由內核支持,但是工具鏈和ABI使得使用起來不方便。


x87僅比當前CPU上的標量SSE數學慢一些。 但是,80位加載/存儲速度非常慢,例如Skylake上的4 uops而不是1( https://agner.org/optimize/ ),以及fld m80的幾個周期額外延遲。

對於你的循環必須通過存儲和使用x87 fild將int轉換為FP,它可能最多比一個好的編譯器可以用SSE2為64位double的速度慢2倍。

當然, long double會阻止自動矢量化。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM